Created
October 1, 2025 13:55
-
-
Save howbizarre/2643b54a2af7c9494f8befe1fd1dd8ba to your computer and use it in GitHub Desktop.
Cloudflare Tail Worker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// hows-logger - Tail Worker за събиране на логове | |
import type { ProcessedLogEntry, TraceItem } from './types'; | |
type ConsoleMethod = 'log' | 'error'; | |
const toIsoString = (timestamp?: number | null): string => { | |
if (!timestamp) { | |
return new Date().toISOString(); | |
} | |
try { | |
return new Date(timestamp).toISOString(); | |
} catch { | |
return new Date().toISOString(); | |
} | |
}; | |
const safeData = (value: unknown) => { | |
try { | |
return JSON.parse(JSON.stringify(value)); | |
} catch { | |
return '[unserializable]'; | |
} | |
}; | |
const emitEntry = (entry: ProcessedLogEntry, method: ConsoleMethod = 'log') => { | |
const prefix = entry.type.toUpperCase(); | |
console[method](`[${prefix}] ${JSON.stringify(entry)}`); | |
}; | |
const handleTraceItem = (trace: TraceItem) => { | |
const { | |
logs = [], | |
outcome, | |
scriptName, | |
eventTimestamp, | |
exceptions = [], | |
diagnosticsChannelEvents = [], | |
event | |
} = trace; | |
const summary = { | |
scriptName, | |
outcome, | |
eventTimestamp: toIsoString(eventTimestamp), | |
logsCount: logs.length, | |
exceptionsCount: exceptions.length, | |
diagnosticsCount: diagnosticsChannelEvents.length | |
}; | |
console.log(`[EVENT] ${JSON.stringify(summary)}`); | |
logs.forEach(({ timestamp, level, message }) => { | |
emitEntry({ | |
type: 'log', | |
timestamp: toIsoString(timestamp), | |
level, | |
message, | |
scriptName, | |
outcome | |
}); | |
}); | |
exceptions.forEach(({ timestamp, name, message }) => { | |
emitEntry({ | |
type: 'exception', | |
timestamp: toIsoString(timestamp), | |
scriptName, | |
name, | |
message | |
}, 'error'); | |
}); | |
diagnosticsChannelEvents.forEach(({ timestamp, channel, message }) => { | |
emitEntry({ | |
type: 'diagnostic', | |
timestamp: toIsoString(timestamp), | |
scriptName, | |
channel, | |
message | |
}); | |
}); | |
if (event) { | |
emitEntry({ | |
type: 'event_info', | |
timestamp: toIsoString(eventTimestamp), | |
scriptName, | |
message: safeData(event), | |
eventType: typeof event | |
}); | |
} | |
}; | |
export default { | |
async tail(events: TraceItem[], _env: unknown, _ctx: unknown) { | |
if (!events || events.length === 0) { | |
return; | |
} | |
console.log(`[TAIL] Получени ${events.length} събития`); | |
for (const trace of events) { | |
try { | |
handleTraceItem(trace); | |
} catch (error) { | |
console.error(`[TAIL_ERROR] Грешка при обработка на събитие: ${error instanceof Error ? error.message : String(error)}`); | |
} | |
} | |
console.log(`[TAIL] Завършена обработка на ${events.length} събития`); | |
} | |
} satisfies ExportedHandler; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// types.ts - Документация на типовете за tail worker | |
// Тези типове са вградени в @cloudflare/workers-types, но ги документираме тук за справка | |
/** | |
* Основен интерфейс за tail worker handler | |
*/ | |
export interface TailHandler { | |
tail(events: TraceItem[], env: Env, ctx: ExecutionContext): Promise<void>; | |
} | |
/** | |
* Основно събитие в tail worker-а | |
*/ | |
export interface TraceItem { | |
/** Име на worker-а, който генерира събитието */ | |
scriptName: string | null; | |
/** Резултат от изпълнението: "ok", "exception", "canceled" */ | |
outcome: string; | |
/** Timestamp на събитието */ | |
eventTimestamp: number | null; | |
/** Информация за HTTP заявка или друго събитие */ | |
event: any; // Реалният тип е по-сложен union type | |
/** Масив от логове (console.log, console.error и т.н.) */ | |
logs: TraceLog[]; | |
/** Масив от изключения и грешки */ | |
exceptions: TraceException[]; | |
/** Масив от diagnostic channel events */ | |
diagnosticsChannelEvents: TraceDiagnosticChannelEvent[]; | |
} | |
/** | |
* Лог запис от console.log, console.error и т.н. | |
*/ | |
export interface TraceLog { | |
/** Timestamp на лога */ | |
timestamp: number; | |
/** Ниво на лога: "log", "error", "warn", "info" */ | |
level: string; | |
/** Съдържание на лога (може да е всякакъв тип) */ | |
message: any; | |
} | |
/** | |
* JavaScript изключение или грешка | |
*/ | |
export interface TraceException { | |
/** Timestamp на грешката */ | |
timestamp: number; | |
/** Име на грешката (напр. "Error", "TypeError") */ | |
name: string; | |
/** Съобщение за грешката */ | |
message: string; | |
} | |
/** | |
* Diagnostic channel event | |
*/ | |
export interface TraceDiagnosticChannelEvent { | |
/** Timestamp на събитието */ | |
timestamp: number; | |
/** Име на diagnostic channel-а */ | |
channel: string; | |
/** Данни от diagnostic event */ | |
message: any; | |
} | |
/** | |
* Формат на обработените логове в нашия worker | |
*/ | |
export interface ProcessedLogEntry { | |
type: 'log' | 'exception' | 'diagnostic' | 'event_info'; | |
timestamp: string; // ISO string | |
scriptName: string | null; | |
level?: string; | |
message: any; | |
outcome?: string; | |
channel?: string; // само за diagnostic events | |
name?: string; // само за exceptions | |
eventType?: string; // само за event_info | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment