@@ -16,7 +16,8 @@ import {
1616} from "@buf/garden_grow-platform.bufbuild_es/garden/public/events/v1/events_pb.js"
1717import type { EventName as CoreEventName , EventPayload as CoreEventPayload } from "../../events/events.js"
1818import type { GardenWithNewBackend } from "../../garden.js"
19- import type { Log } from "../../logger/log-entry.js"
19+ import type { LogSymbol , Msg } from "../../logger/log-entry.js"
20+ import { isActionLogContext , isCoreLogContext , type Log } from "../../logger/log-entry.js"
2021import { monotonicFactory , ulid , type ULID , type UUID } from "ulid"
2122import { create } from "@bufbuild/protobuf"
2223import {
@@ -49,6 +50,12 @@ import {
4950 DeployRunResultSchema ,
5051} from "@buf/garden_grow-platform.bufbuild_es/garden/public/events/v1/garden_action_pb.js"
5152import type { DeployStatusForEventPayload } from "../../types/service.js"
53+ import {
54+ DataFormat ,
55+ GardenLogMessageEmittedSchema ,
56+ LogSymbol as GrpcLogSymbol ,
57+ } from "@buf/garden_grow-platform.bufbuild_es/garden/public/events/v1/garden_logs_pb.js"
58+ import type { LogEntryEventPayload } from "../api-legacy/restful-event-stream.js"
5259
5360const nextEventUlid = monotonicFactory ( )
5461
@@ -73,6 +80,7 @@ const aecEnvironmentUpdateActionTriggeredMap = {
7380export class GrpcEventConverter {
7481 private readonly garden : GardenWithNewBackend
7582 private readonly log : Log
83+ private readonly shouldStreamLogEntries : boolean
7684
7785 /**
7886 * It is important to keep it static,
@@ -81,9 +89,10 @@ export class GrpcEventConverter {
8189 */
8290 private static readonly uuidToUlidMap = new Map < UUID , ULID > ( )
8391
84- constructor ( garden : GardenWithNewBackend , log : Log ) {
92+ constructor ( garden : GardenWithNewBackend , log : Log , shouldStreamLogEntries : boolean ) {
8593 this . garden = garden
8694 this . log = log
95+ this . shouldStreamLogEntries = shouldStreamLogEntries
8796 }
8897
8998 convert < T extends CoreEventName > ( name : T , payload : CoreEventPayload < T > ) : GrpcEventEnvelope [ ] {
@@ -132,18 +141,66 @@ export class GrpcEventConverter {
132141 payload : payload as CoreEventPayload < "aecAgentStatus" > ,
133142 } )
134143 break
144+ // NOTE: We're not propagating "log" events, only keeping those for legacy Cloud
145+ case "logEntry" :
146+ events = this . handleLogEntry ( { context, payload : payload as CoreEventPayload < "logEntry" > } )
147+ break
135148 default :
136- // TODO: handle all event cases
137- // name satisfies never // ensure all cases are handled
138- this . log . silly ( `GrpcEventStream: Unhandled core event ${ name } ` )
139149 return [ ]
140150 }
141151
142- if ( events . length === 0 ) {
143- this . log . silly ( `GrpcEventStream: Ignoring core event ${ name } ` )
152+ return events
153+ }
154+
155+ private handleLogEntry ( {
156+ context,
157+ payload,
158+ } : {
159+ context : GardenEventContext
160+ payload : LogEntryEventPayload
161+ } ) : GrpcEventEnvelope [ ] {
162+ if ( ! this . shouldStreamLogEntries ) {
163+ return [ ]
144164 }
165+ const msg = resolveMsg ( payload . message . msg )
166+ let rawMsg = resolveMsg ( payload . message . rawMsg )
145167
146- return events
168+ if ( msg === rawMsg ) {
169+ // No need to send both if they're the same
170+ rawMsg = undefined
171+ }
172+
173+ const coreLog = isCoreLogContext ( payload . context ) ? payload . context : undefined
174+ const actionLog = isActionLogContext ( payload . context ) ? payload . context : undefined
175+
176+ let actionUlid : ULID | undefined = undefined
177+ if ( actionLog ) {
178+ actionUlid = this . mapToUlid ( actionLog . actionUid , "actionUid" , "actionUlid" )
179+ }
180+
181+ return [
182+ createGardenCliEvent ( context , GardenCliEventType . LOGS_EMITTED , {
183+ case : "logsEmitted" ,
184+ value : create ( GardenLogMessageEmittedSchema , {
185+ actionUlid,
186+ loggedAt : timestampFromDate ( new Date ( payload . timestamp ) ) ,
187+ logLevel : payload . level + 1 ,
188+ // NOTE: We deprecated the sectionName and logMessage fields, preferring the logDetails field instead
189+ originDescription : payload . context . origin ,
190+ logDetails : {
191+ // Empty strings should be omitted
192+ msg : msg ?? undefined ,
193+ rawMsg : rawMsg ?? undefined ,
194+ data : payload . message . data ?? undefined ,
195+ dataFormat : convertLogMessageDataFormat ( payload . message . dataFormat ) ,
196+ symbol : convertLogSymbol ( payload . message . symbol ) ,
197+ error : payload . message . error ,
198+ coreLog,
199+ actionLog,
200+ } ,
201+ } ) ,
202+ } ) ,
203+ ]
147204 }
148205
149206 private handleAecEnvironmentUpdate ( {
@@ -530,3 +587,38 @@ export function createGardenCliEvent(
530587export function describeGrpcEvent ( event : GrpcEventEnvelope ) : string {
531588 return `GrpcEvent(${ event . eventUlid } , ${ event . eventData . case } , ${ event . eventData . value ?. eventData . case } )`
532589}
590+
591+ function convertLogSymbol ( symbol : LogSymbol | undefined ) : GrpcLogSymbol | undefined {
592+ switch ( symbol ) {
593+ case "info" :
594+ return GrpcLogSymbol . INFO
595+ case "success" :
596+ return GrpcLogSymbol . SUCCESS
597+ case "warning" :
598+ return GrpcLogSymbol . WARNING
599+ case "error" :
600+ return GrpcLogSymbol . ERROR
601+ case "empty" :
602+ return GrpcLogSymbol . UNSPECIFIED
603+ default :
604+ return undefined
605+ }
606+ }
607+
608+ function convertLogMessageDataFormat ( dataFormat : "json" | "yaml" | undefined ) : DataFormat | undefined {
609+ switch ( dataFormat ) {
610+ case "json" :
611+ return DataFormat . JSON
612+ case "yaml" :
613+ return DataFormat . YAML
614+ default :
615+ return undefined
616+ }
617+ }
618+
619+ function resolveMsg ( msg : Msg | undefined ) : string | undefined {
620+ if ( typeof msg === "function" ) {
621+ return msg ( )
622+ }
623+ return msg
624+ }
0 commit comments