File tree Expand file tree Collapse file tree
apps/supervisor/src/wideEvents Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -35,6 +35,26 @@ describe("emit", () => {
3535 expect ( out . duration_ms ) . toBe ( 5 ) ;
3636 } ) ;
3737
38+ it ( "emits start_time as an ISO timestamp set by newState" , ( ) => {
39+ const s = newState ( { service : "supervisor" , env : { } } ) ;
40+ s . statusCode = 200 ;
41+ s . ok = true ;
42+ const out = captureEmit ( s ) ;
43+ expect ( typeof out . start_time ) . toBe ( "string" ) ;
44+ // Microsecond-precision RFC3339 (6 fractional digits), parseable as a date.
45+ expect ( out . start_time ) . toMatch ( / ^ \d { 4 } - \d { 2 } - \d { 2 } T \d { 2 } : \d { 2 } : \d { 2 } \. \d { 6 } Z $ / ) ;
46+ expect ( Number . isNaN ( new Date ( out . start_time as string ) . getTime ( ) ) ) . toBe ( false ) ;
47+ } ) ;
48+
49+ it ( "omits start_time when unset" , ( ) => {
50+ const s = newState ( { service : "supervisor" , env : { } } ) ;
51+ delete s . startTime ;
52+ s . statusCode = 200 ;
53+ s . ok = true ;
54+ const out = captureEmit ( s ) ;
55+ expect ( out ) . not . toHaveProperty ( "start_time" ) ;
56+ } ) ;
57+
3858 it ( "omits empty optional fields" , ( ) => {
3959 const s = newState ( { service : "supervisor" , env : { } } ) ;
4060 s . statusCode = 200 ;
Original file line number Diff line number Diff line change @@ -21,6 +21,7 @@ export function emit(state: State): void {
2121 } ;
2222
2323 if ( state . traceId ) out . trace_id = state . traceId ;
24+ appendIfSet ( out , "start_time" , state . startTime ) ;
2425 appendIfSet ( out , "service" , state . service ) ;
2526 appendIfSet ( out , "version" , state . version ) ;
2627 appendIfSet ( out , "commit_sha" , state . commitSha ) ;
Original file line number Diff line number Diff line change @@ -58,6 +58,7 @@ export function newState(opts: NewStateOptions): State {
5858 const requestId = isValidRequestId ( inbound ) ? inbound : newRequestId ( ) ;
5959
6060 return {
61+ startTime : nowRfc3339 ( ) ,
6162 requestId,
6263 traceId : parseTraceId ( traceparent ) ,
6364 traceparent,
@@ -80,3 +81,16 @@ export function newState(opts: NewStateOptions): State {
8081function newRequestId ( ) : string {
8182 return "req-" + randomBytes ( 16 ) . toString ( "hex" ) ;
8283}
84+
85+ /**
86+ * Current wall-clock time as an RFC3339 string with microsecond precision.
87+ * `Date.toISOString()` only has millisecond resolution, which is too coarse to
88+ * order multiple wide events emitted within the same millisecond.
89+ * `performance.timeOrigin + performance.now()` gives a sub-millisecond wall-clock
90+ * reading; we append the microsecond digits to the millisecond ISO string.
91+ */
92+ function nowRfc3339 ( ) : string {
93+ const ms = performance . timeOrigin + performance . now ( ) ;
94+ const micros = Math . floor ( ( ms % 1 ) * 1000 ) ; // microseconds within the millisecond (0..999)
95+ return new Date ( ms ) . toISOString ( ) . slice ( 0 , - 1 ) + String ( micros ) . padStart ( 3 , "0" ) + "Z" ;
96+ }
Original file line number Diff line number Diff line change 55 * events stay compact.
66 */
77export type State = {
8+ /**
9+ * Wall-clock time the event began, as an ISO-8601 string. Emitted as
10+ * `start_time` so log collection orders events by when work started rather
11+ * than by the collector's ingestion time.
12+ */
13+ startTime ?: string ;
14+
815 // Cross-stack correlation.
916 requestId : string ;
1017 traceId : string ;
You can’t perform that action at this time.
0 commit comments