- 规范sentry数据收集标签

- 添加切割时间点结束时间必须大于开始时间的检查
This commit is contained in:
shuohigh@gmail.com 2025-05-27 13:26:13 +08:00
parent c87232512b
commit e5029a7922
5 changed files with 36 additions and 21 deletions

View File

@ -1,4 +1,4 @@
MODAL_ENVIRONMENT=dev MODAL_ENVIRONMENT=test
modal_app_name=bowong-ai-video modal_app_name=bowong-ai-video
S3_mount_dir=/mntS3 S3_mount_dir=/mntS3
S3_bucket_name=modal-media-cache S3_bucket_name=modal-media-cache

View File

@ -1,5 +1,5 @@
from typing import Union, Any from typing import Union, Any
from pydantic import BaseModel, Field, computed_field, field_validator from pydantic import BaseModel, Field, computed_field, field_validator, model_validator
from pydantic.json_schema import JsonSchemaValue from pydantic.json_schema import JsonSchemaValue
from ..utils.TimeUtils import TimeDelta from ..utils.TimeUtils import TimeDelta
@ -43,6 +43,12 @@ class FFMpegSliceSegment(BaseModel):
else: else:
raise TypeError(v) raise TypeError(v)
@model_validator(mode='after')
def validate_end_after_start(self) -> 'FFMpegSliceSegment':
if self.end <= self.start:
raise ValueError("end time must be greater than start time")
return self
@classmethod @classmethod
def __get_pydantic_json_schema__(cls, core_schema: Any, handler: Any) -> JsonSchemaValue: def __get_pydantic_json_schema__(cls, core_schema: Any, handler: Any) -> JsonSchemaValue:
# Override the schema to represent it as a string # Override the schema to represent it as a string

View File

@ -201,7 +201,14 @@ class MediaSources(BaseModel):
@classmethod @classmethod
def parse_inputs(cls, v: Union[str, MediaSource]) -> List[MediaSource]: def parse_inputs(cls, v: Union[str, MediaSource]) -> List[MediaSource]:
if not v: if not v:
raise ValidationError("inputs为空") raise ValidationError([
{
'loc': ('inputs',),
'msg': "inputs为空",
'type': 'value_error',
'input': v
}
], MediaSources)
result = [] result = []
for item in v: for item in v:
if isinstance(item, str): if isinstance(item, str):
@ -209,7 +216,14 @@ class MediaSources(BaseModel):
elif isinstance(item, MediaSource): elif isinstance(item, MediaSource):
result.append(item) result.append(item)
else: else:
raise ValidationError("inputs元素类型错误: 必须是字符串") raise ValidationError([
{
'loc': ('inputs',),
'msg': "inputs元素类型错误: 必须是字符串",
'type': 'value_error',
'input': v
}
], MediaSources)
return result return result
model_config = { model_config = {

View File

@ -32,7 +32,7 @@ class SentryUtils:
op=op, op=op,
name=name) name=name)
else: else:
transaction = sentry_sdk.start_transaction(op='modal.function', name='Modal Function直接调用') transaction = sentry_sdk.start_transaction(op=op, name=name)
with transaction: with transaction:
with transaction.start_child(op=op, name=name) as span: with transaction.start_child(op=op, name=name) as span:
cpu_freq, cpu_count, mem = SentryUtils.capture_hardware_info() cpu_freq, cpu_count, mem = SentryUtils.capture_hardware_info()
@ -43,23 +43,18 @@ class SentryUtils:
'x_trace.id': sentry_trace_id, 'x_trace.id': sentry_trace_id,
'x_trace.baggage': sentry_baggage, 'x_trace.baggage': sentry_baggage,
'cpu.count': cpu_count, 'cpu.count': cpu_count,
'cpu.frequency': cpu_freq.current / 1000, 'cpu.frequency': f"{cpu_freq.current / 1000:.2f}",
'cpu.frequency.format': f"{cpu_freq.current / 1000:.2f} GHz", 'cpu.frequency.format': f"{cpu_freq.current / 1000:.2f} GHz",
'memory.available': total_mb, 'memory.available': total_mb,
'memory.available.format': f"{total_mb} Mb", 'memory.available.format': f"{total_mb} Mb",
'cloud_resource': { 'modal.cloud.region': os.environ.get('MODAL_REGION', 'unknown'),
"cloud.provider": 'Modal', 'modal.cloud.provider': os.environ.get('MODAL_CLOUD_PROVIDER', 'unknown'),
"type": "cloud_resource"
},
'modal': {
'modal.region': os.environ.get('MODAL_REGION', 'unknown'),
'modal.provider': os.environ.get('MODAL_CLOUD_PROVIDER', 'unknown'),
'modal.task.id': os.environ.get('MODAL_TASK_ID', 'unknown'), 'modal.task.id': os.environ.get('MODAL_TASK_ID', 'unknown'),
'modal.identity.token': os.environ.get('MODAL_IDENTITY_TOKEN', 'unknown'), 'modal.identity.token': os.environ.get('MODAL_IDENTITY_TOKEN', 'unknown'),
'modal.image.id': os.environ.get('MODAL_IMAGE_ID', 'unknown'), 'modal.image.id': os.environ.get('MODAL_IMAGE_ID', 'unknown'),
}
}) })
result = func(*args, **kwargs) result = func(*args, **kwargs)
span.set_status('ok')
return result return result
return wrapper return wrapper

View File

@ -156,7 +156,7 @@ with ffmpeg_worker_image.imports():
List[str], Optional[SentryTransactionInfo]]: List[str], Optional[SentryTransactionInfo]]:
fn_id = current_function_call_id() fn_id = current_function_call_id()
@SentryUtils.sentry_tracker(name="视频切割任务", op="ffmpeg.slice", fn_id=fn_id, @SentryUtils.sentry_tracker(name="视频文件切割任务", op="ffmpeg.slice.media", fn_id=fn_id,
sentry_trace_id=sentry_trace.x_trace_id if sentry_trace else None, sentry_trace_id=sentry_trace.x_trace_id if sentry_trace else None,
sentry_baggage=sentry_trace.x_baggage if sentry_trace else None) sentry_baggage=sentry_trace.x_baggage if sentry_trace else None)
@SentryUtils.webhook_handler(webhook=webhook, func_id=fn_id) @SentryUtils.webhook_handler(webhook=webhook, func_id=fn_id)
@ -164,15 +164,15 @@ with ffmpeg_worker_image.imports():
fn_id: str) -> List[FFMPEGResult]: fn_id: str) -> List[FFMPEGResult]:
cache_filepath = f"{s3_mount}/{media_source.cache_filepath}" cache_filepath = f"{s3_mount}/{media_source.cache_filepath}"
logger.info(f"{media_source.urn}切割") logger.info(f"{media_source.urn}切割")
for marker in media_markers: for i, marker in enumerate(media_markers):
logger.info(f"{marker.start.toFormatStr()} --> {marker.end.toFormatStr()}") logger.info(f"[{i}] {marker.start.toFormatStr()} --> {marker.end.toFormatStr()}")
segments = await VideoUtils.ffmpeg_slice_media(media_path=cache_filepath, segments = await VideoUtils.ffmpeg_slice_media(media_path=cache_filepath,
media_markers=media_markers, media_markers=media_markers,
output_path=f"{output_path_prefix}/{config.modal_environment}/slice/outputs/{fn_id}/output.mp4") output_path=f"{output_path_prefix}/{config.modal_environment}/slice/outputs/{fn_id}/output.mp4")
return [FFMPEGResult(urn=local_copy_to_s3([segment[0]])[0], metadata=segment[1], return [FFMPEGResult(urn=local_copy_to_s3([segment[0]])[0], metadata=segment[1],
content_length=os.path.getsize(segment[0])) for segment in segments] content_length=os.path.getsize(segment[0])) for segment in segments]
@SentryUtils.sentry_tracker(name="视频切割任务", op="ffmpeg.slice", fn_id=fn_id, @SentryUtils.sentry_tracker(name="直播视频切割任务", op="ffmpeg.slice.stream", fn_id=fn_id,
sentry_trace_id=sentry_trace.x_trace_id if sentry_trace else None, sentry_trace_id=sentry_trace.x_trace_id if sentry_trace else None,
sentry_baggage=sentry_trace.x_baggage if sentry_trace else None) sentry_baggage=sentry_trace.x_baggage if sentry_trace else None)
@SentryUtils.webhook_handler(webhook=webhook, func_id=fn_id) @SentryUtils.webhook_handler(webhook=webhook, func_id=fn_id)