From 486d6818ba00a58045546e7b447e6c029a646d90 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Mon, 3 Jul 2023 16:00:11 +0100 Subject: [PATCH] SDK: IO Logging now respects the job and client logLevel, and only outputs locally when ioLogLocalEnabled is true --- .changeset/bright-buses-join.md | 5 ++ examples/nextjs-example/src/jobs/logging.ts | 19 ++++++ .../nextjs-example/src/pages/api/trigger.ts | 1 + examples/nextjs-example/src/trigger.ts | 3 +- packages/internal/src/logger.ts | 4 ++ packages/trigger-sdk/src/io.ts | 58 +++++++++++++------ packages/trigger-sdk/src/job.ts | 5 ++ packages/trigger-sdk/src/triggerClient.ts | 39 +++++++++---- 8 files changed, 103 insertions(+), 31 deletions(-) create mode 100644 .changeset/bright-buses-join.md create mode 100644 examples/nextjs-example/src/jobs/logging.ts diff --git a/.changeset/bright-buses-join.md b/.changeset/bright-buses-join.md new file mode 100644 index 000000000..3a407b2fc --- /dev/null +++ b/.changeset/bright-buses-join.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/sdk": patch +--- + +IO Logging now respects the job and client logLevel, and only outputs locally when ioLogLocalEnabled is true diff --git a/examples/nextjs-example/src/jobs/logging.ts b/examples/nextjs-example/src/jobs/logging.ts new file mode 100644 index 000000000..5c3486248 --- /dev/null +++ b/examples/nextjs-example/src/jobs/logging.ts @@ -0,0 +1,19 @@ +import { client } from "@/trigger"; +import { Job, eventTrigger } from "@trigger.dev/sdk"; + +new Job(client, { + id: "test-logging", + name: "Test logging", + version: "0.0.1", + logLevel: "debug", + trigger: eventTrigger({ + name: "test.logging", + }), + run: async (payload, io, ctx) => { + await io.logger.log("Hello log level", { payload }); + await io.logger.error("Hello error level", { payload }); + await io.logger.warn("Hello warn level", { payload }); + await io.logger.info("Hello info level", { payload }); + await io.logger.debug("Hello debug level", { payload }); + }, +}); diff --git a/examples/nextjs-example/src/pages/api/trigger.ts b/examples/nextjs-example/src/pages/api/trigger.ts index f579c2033..4f6c1bd12 100644 --- a/examples/nextjs-example/src/pages/api/trigger.ts +++ b/examples/nextjs-example/src/pages/api/trigger.ts @@ -4,6 +4,7 @@ import "@/jobs/openai"; import "@/jobs/resend"; import "@/jobs/general"; import "@/jobs/slack"; +import "@/jobs/logging"; import { createPagesRoute } from "@trigger.dev/nextjs"; const { handler, config } = createPagesRoute(client); diff --git a/examples/nextjs-example/src/trigger.ts b/examples/nextjs-example/src/trigger.ts index 8bc41d5af..ef03aac32 100644 --- a/examples/nextjs-example/src/trigger.ts +++ b/examples/nextjs-example/src/trigger.ts @@ -16,7 +16,8 @@ export const client = new TriggerClient({ id: "nextjs-example", apiKey: process.env.TRIGGER_API_KEY, apiUrl: process.env.TRIGGER_API_URL, - logLevel: "debug", + verbose: false, + ioLogLocalEnabled: true, }); export const openai = new OpenAI({ diff --git a/packages/internal/src/logger.ts b/packages/internal/src/logger.ts index 4eeea12d3..60074fbd2 100644 --- a/packages/internal/src/logger.ts +++ b/packages/internal/src/logger.ts @@ -35,6 +35,10 @@ export class Logger { ); } + static satisfiesLogLevel(logLevel: LogLevel, setLevel: LogLevel) { + return logLevels.indexOf(logLevel) <= logLevels.indexOf(setLevel); + } + log(...args: any[]) { if (this.#level < 0) return; diff --git a/packages/trigger-sdk/src/io.ts b/packages/trigger-sdk/src/io.ts index 1fd5dd9bd..89822e52c 100644 --- a/packages/trigger-sdk/src/io.ts +++ b/packages/trigger-sdk/src/io.ts @@ -43,6 +43,8 @@ export type IOOptions = { context: TriggerContext; logger?: Logger; logLevel?: LogLevel; + jobLogger?: Logger; + jobLogLevel: LogLevel; cachedTasks?: Array; }; @@ -51,6 +53,8 @@ export class IO { private _apiClient: ApiClient; private _triggerClient: TriggerClient; private _logger: Logger; + private _jobLogger?: Logger; + private _jobLogLevel: LogLevel; private _cachedTasks: Map; private _taskStorage: AsyncLocalStorage<{ taskId: string }>; private _context: TriggerContext; @@ -62,6 +66,8 @@ export class IO { this._logger = options.logger ?? new Logger("trigger.dev", options.logLevel); this._cachedTasks = new Map(); + this._jobLogger = options.jobLogger; + this._jobLogLevel = options.jobLogLevel; if (options.cachedTasks) { options.cachedTasks.forEach((task) => { @@ -75,38 +81,51 @@ export class IO { get logger() { return new IOLogger(async (level, message, data) => { + let logLevel: LogLevel = "info"; + switch (level) { + case "LOG": { + this._jobLogger?.log(message, data); + logLevel = "log"; + break; + } case "DEBUG": { - this._logger.debug(message, data); + this._jobLogger?.debug(message, data); + logLevel = "debug"; break; } case "INFO": { - this._logger.info(message, data); + this._jobLogger?.info(message, data); + logLevel = "info"; break; } case "WARN": { - this._logger.warn(message, data); + this._jobLogger?.warn(message, data); + logLevel = "warn"; break; } case "ERROR": { - this._logger.error(message, data); + this._jobLogger?.error(message, data); + logLevel = "error"; break; } } - await this.runTask( - [message, level], - { - name: "log", - icon: "log", - description: message, - params: data, - properties: [{ label: "Level", text: level }], - style: { style: "minimal", variant: level.toLowerCase() }, - noop: true, - }, - async (task) => {} - ); + if (Logger.satisfiesLogLevel(logLevel, this._jobLogLevel)) { + await this.runTask( + [message, level], + { + name: "log", + icon: "log", + description: message, + params: data, + properties: [{ label: "Level", text: level }], + style: { style: "minimal", variant: level.toLowerCase() }, + noop: true, + }, + async (task) => {} + ); + } }); } @@ -595,7 +614,7 @@ function stableStringify(obj: any): string { } type CallbackFunction = ( - level: "DEBUG" | "INFO" | "WARN" | "ERROR", + level: "DEBUG" | "INFO" | "WARN" | "ERROR" | "LOG", message: string, properties?: Record ) => Promise; @@ -603,6 +622,9 @@ type CallbackFunction = ( export class IOLogger implements TaskLogger { constructor(private callback: CallbackFunction) {} + log(message: string, properties?: Record): Promise { + return this.callback("LOG", message, properties); + } debug(message: string, properties?: Record): Promise { return this.callback("DEBUG", message, properties); } diff --git a/packages/trigger-sdk/src/job.ts b/packages/trigger-sdk/src/job.ts index c658e134e..b8afc7fe8 100644 --- a/packages/trigger-sdk/src/job.ts +++ b/packages/trigger-sdk/src/job.ts @@ -12,6 +12,7 @@ import { import { TriggerClient } from "./triggerClient"; import type { EventSpecification, + Logger, Trigger, TriggerContext, TriggerEventType, @@ -103,6 +104,10 @@ export class Job< ); } + get logLevel() { + return this.options.logLevel; + } + toJSON(): JobMetadata { // @ts-ignore const internal = this.options.__internal as JobMetadata["internal"]; diff --git a/packages/trigger-sdk/src/triggerClient.ts b/packages/trigger-sdk/src/triggerClient.ts index a7749f35d..e1f5b91b8 100644 --- a/packages/trigger-sdk/src/triggerClient.ts +++ b/packages/trigger-sdk/src/triggerClient.ts @@ -57,6 +57,8 @@ export type TriggerClientOptions = { running your own Trigger.dev instance. */ apiUrl?: string; logLevel?: LogLevel; + verbose?: boolean; + ioLogLocalEnabled?: boolean; }; /** A [TriggerClient](https://trigger.dev/docs/documentation/concepts/client-adaptors) is used to connect to a specific [Project](https://trigger.dev/docs/documentation/concepts/projects) by using an [API Key](https://trigger.dev/docs/documentation/concepts/environments-apikeys). */ @@ -87,18 +89,21 @@ export class TriggerClient { {}; #client: ApiClient; - #logger: Logger; + #internalLogger: Logger; id: string; constructor(options: Prettify) { this.id = options.id; this.#options = options; this.#client = new ApiClient(this.#options); - this.#logger = new Logger("trigger.dev", this.#options.logLevel); + this.#internalLogger = new Logger( + "trigger.dev", + this.#options.verbose ? "debug" : "log" + ); } async handleRequest(request: Request): Promise { - this.#logger.debug("handling request", { + this.#internalLogger.debug("handling request", { url: request.url, headers: Object.fromEntries(request.headers.entries()), method: request.method, @@ -455,7 +460,7 @@ export class TriggerClient { params: any; }): void { this.#registeredHttpSourceHandlers[options.key] = async (s, r) => { - return await options.source.handle(s, r, this.#logger); + return await options.source.handle(s, r, this.#internalLogger); }; let registeredSource = this.#registeredSources[options.key]; @@ -593,7 +598,10 @@ export class TriggerClient { body: RunJobBody, job: Job, any> ): Promise { - this.#logger.debug("executing job", { execution: body, job: job.toJSON() }); + this.#internalLogger.debug("executing job", { + execution: body, + job: job.toJSON(), + }); const context = this.#createRunContext(body); @@ -601,9 +609,13 @@ export class TriggerClient { id: body.run.id, cachedTasks: body.tasks, apiClient: this.#client, - logger: this.#logger, + logger: this.#internalLogger, client: this, context, + jobLogLevel: job.logLevel ?? this.#options.logLevel ?? "info", + jobLogger: this.#options.ioLogLocalEnabled + ? new Logger(job.id, job.logLevel ?? this.#options.logLevel ?? "info") + : undefined, }); const ioWithConnections = createIOWithIntegrations( @@ -714,7 +726,7 @@ export class TriggerClient { }, sourceRequest: Request ): Promise<{ response: NormalizedResponse; events: SendEvent[] }> { - this.#logger.debug("Handling HTTP source request", { + this.#internalLogger.debug("Handling HTTP source request", { source, }); @@ -722,9 +734,12 @@ export class TriggerClient { const dynamicTrigger = this.#registeredDynamicTriggers[source.dynamicId]; if (!dynamicTrigger) { - this.#logger.debug("No dynamic trigger registered for HTTP source", { - source, - }); + this.#internalLogger.debug( + "No dynamic trigger registered for HTTP source", + { + source, + } + ); return { response: { @@ -740,7 +755,7 @@ export class TriggerClient { const results = await dynamicTrigger.source.handle( source, sourceRequest, - this.#logger + this.#internalLogger ); if (!results) { @@ -769,7 +784,7 @@ export class TriggerClient { const handler = this.#registeredHttpSourceHandlers[source.key]; if (!handler) { - this.#logger.debug("No handler registered for HTTP source", { + this.#internalLogger.debug("No handler registered for HTTP source", { source, });