👋
Welcome to Workers Loki Logger
A Logger for the Cloudflare workers environment that sends the logs to grafana loki
Install
npm install workers-loki-logger
Usage
When creating a logger and writing to it all logs are first stored in memory.
It is sent to loki when the flush method is called.
It is recommended to use a wrapper function for the logger to ensure that the logs are flushed to loki.
Features
- Send logs to loki
- Format exceptions as string
- MDC to add additional information to the logs
API
new Logger()
Creates a new logger
import {Logger} from 'workers-loki-logger';
new Logger({
cloudflareContext: context, // Cloudflare context if this option is set it will use the waitUntil function on flush
lokiSecret: 'some-secret', // Secret for loki authentication
lokiUrl: 'https://logs-prod-eu-west-0.grafana.net', // Url to loki
stream: { // Stream options for loki
worker: 'esm-worker-simple',
environment: 'development',
},
mdc: { // If this option is set it will be used as the initial value for the MDC
requestPath: '/some/path',
}
});
logger.{info,warn,error,fatal}()
Write log
import {Logger} from 'workers-loki-logger';
declare const logger: Logger;
logger.info('message');
logger.warn('message');
logger.warn('message', new Error('some error'));
logger.error('message');
logger.error('message', new Error('some error'));
logger.fatal('message');
logger.fatal('message', new Error('some error'));
logger.{mdcSet,mdcDelete,mdcGet}()
Change MDC
import {Logger} from 'workers-loki-logger';
declare const logger: Logger;
logger.mdcSet('key', 'value');
logger.mdcGet('key');
logger.mdcDelete('key');
logger.flush()
Write logs to loki
import {Logger} from 'workers-loki-logger';
declare const logger: Logger;
await logger.flush();
Example
Example with wrapper function
import {Logger} from 'workers-loki-logger';
function getLogger(context: ExecutionContext, lokiSecret: string) {
return new Logger({
cloudflareContext: context,
lokiSecret,
lokiUrl: 'https://logs-prod-eu-west-0.grafana.net',
stream: {
worker: 'esm-worker-simple',
environment: 'development',
}
});
}
type Environment = {
LOKI_SECRET: string
}
// This function could be moved to a separate file and imported by multiple workers
function onFetch(handler: (request: Request, environment: Environment, context: ExecutionContext, logger: Logger) => Promise<Response>) {
return async (request: Request, environment: Environment, context: ExecutionContext) => {
const logger = getLogger(context, environment.LOKI_SECRET);
logger.mdcSet('requestUrl', request.url);
let response;
try {
response = await handler(request, environment, context, logger);
} catch (error) {
logger.error('Caught error', error);
response = new Response('Internal Server Error', {status: 500});
} finally {
await logger.flush();
}
return response;
};
}
export default {
fetch: onFetch(async (request, environment, context, logger) => {
logger.info('Request received' + request.url);
return await fetch(request);
})
};
Example with manually calling flush
import {Logger} from 'workers-loki-logger';
function getLogger(context: ExecutionContext, lokiSecret: string) {
return new Logger({
cloudflareContext: context,
lokiSecret,
lokiUrl: 'https://logs-prod-eu-west-0.grafana.net',
stream: {
worker: 'esm-worker-simple',
environment: 'development',
}
});
}
export default {
async fetch(request: Request, environment: { LOKI_SECRET: string }, context: ExecutionContext) {
const logger = getLogger(context, environment.LOKI_SECRET);
logger.mdcSet('requestUrl', request.url);
logger.info('Request received' + request.url);
let response;
try {
response = await fetch(request);
} catch (error) {
logger.error('Caught error', error);
response = new Response('Internal Server Error', {status: 500});
} finally {
await logger.flush();
}
return response;
}
};
Run tests
npm test