@shutter-network/concorde/logging
Where a part of a Gateway says what it is doing: one small interface, and the default that satisfies it.
Logger is what every part takes, and LogFields is what a line carries beside its message. defaultLogger builds the one a part logs through when the Operator passes nothing.
A logger is per part and never global. Each constructor takes its own, so one logger everywhere means handing one object to each of them, and a part left with the default writes to stdout while the rest write wherever you sent them. createGateway's logger option is the Signal Worker's alone.
This subpath has no Component and no route, it does not use the Db, and it exports no schema.
Example
A logger of the deployment's own, given to two parts that each take theirs separately.
import { createGateway } from "@shutter-network/concorde/gateway";
import type { LogFields, Logger } from "@shutter-network/concorde/logging";
import { createPiRuntime } from "@shutter-network/concorde/pi";
import { createScheduler } from "@shutter-network/concorde/scheduler";
// Four methods, and nothing to inherit from or register with.
const toConsole: Logger = {
debug: () => {},
info: (fields: LogFields, message: string) => console.log(message, fields),
warn: (fields: LogFields, message: string) => console.warn(message, fields),
error: (fields: LogFields, message: string) => console.error(message, fields),
};
const gateway = createGateway({
databaseUrl: process.env.DATABASE_URL ?? "",
runtime: createPiRuntime({ image: "my-agent:1" }),
// Not loopback: the agent reaches this server from a container of its own.
agentListen: { host: "0.0.0.0", port: 8081 },
publicListen: { host: "0.0.0.0", port: 8080 },
// The Signal Worker's, and nothing else's.
logger: toConsole,
extend: ({ db, worker, agentServer }) => ({
// Stated again here, because a component built by hand takes its own.
scheduler: createScheduler({ db, worker, agentServer, logger: toConsole }),
}),
handlers: () => ({}),
});
await gateway.start();Type Aliases
LogFields
type LogFields = Record<string, unknown>;Logger
type Logger = {
debug: (fields: LogFields, message: string) => void;
error: (fields: LogFields, message: string) => void;
info: (fields: LogFields, message: string) => void;
warn: (fields: LogFields, message: string) => void;
};What every part of a Gateway logs through. Four levels, and any object carrying them satisfies it, so a deployment that logs elsewhere passes its own object instead of adapting one.
fatal and trace are pino's and are left out. Nothing here has a use for either, and their absence is what keeps a hand-written logger four methods long.
Methods
debug()
debug(fields: LogFields, message: string): void;Parameters
fields
message
string
Returns
void
error()
error(fields: LogFields, message: string): void;Parameters
fields
message
string
Returns
void
info()
info(fields: LogFields, message: string): void;Parameters
fields
message
string
Returns
void
warn()
warn(fields: LogFields, message: string): void;Parameters
fields
message
string
Returns
void
Functions
defaultLogger()
function defaultLogger(): Logger;What a part logs through when the Operator supplies nothing: pino, writing JSON lines to stdout at info.
Typed as Logger and not as a pino logger, so nothing in a deployment's own code ends up holding pino's types. Everything below info is dropped, and debug is where the parts write what they are doing, so a deployment that wants those lines configures pino itself and passes the result.