@sidhant_001/traceer
v0.1.0
Published
Centralized Winston-based logging SDK for Node.js with AWS CloudWatch, GCP Cloud Logging and per-request context.
Maintainers
Readme
traceer
Centralized, cloud-aware logging for Node.js, built on winston.
One logger API that writes the right format for wherever your service runs:
| Provider | Default output | Optional direct upload |
| --- | --- | --- |
| local | Coloured, pretty console (+ rotating files) | – |
| aws (Lambda, ECS, EKS, EC2) | JSON on stdout, which CloudWatch Logs collects | CloudWatch Logs API (aws.logGroupName) |
| gcp (Cloud Run, GKE, Functions, App Engine) | Structured JSON on stdout with severity + trace, which Cloud Logging collects | Cloud Logging API (gcp.useCloudLoggingApi) |
It also includes:
- Per-request context: every log inside a request gets the same
requestIdandtraceId(fromtraceparent,x-cloud-trace-contextorx-amzn-trace-id). - Request summary logs: one log per request with method, url, status and duration.
- Redaction of secrets (
password,token,authorization, …) at any depth. - Error serialization:
logger.error("msg", err)keepsmessage,stack,causeandcode. - Graceful shutdown:
await logger.close()flushes pending cloud uploads.
Install
npm install @sidhant_001/traceerTo upload directly through a cloud API (optional; stdout is enough on managed platforms):
npm install winston-cloudwatch @aws-sdk/client-cloudwatch-logs # AWS
npm install @google-cloud/logging-winston # GCPQuick start
import express from "express";
import { createLogger, requestLogger } from "@sidhant_001/traceer";
const logger = createLogger({ service: "orders-api" });
const app = express();
app.use(requestLogger(logger, { ignorePaths: ["/health"] }));
app.get("/orders/:id", (req, res) => {
logger.info("Fetching order", { orderId: req.params.id }); // gets requestId automatically
res.json({ id: req.params.id });
});
app.listen(3000, () => logger.info("Server started"));
process.on("SIGTERM", async () => {
await logger.close();
process.exit(0);
});requestLogger works with Express, Connect and plain http servers.
Options
createLogger({
service: "orders-api", // env SERVICE_NAME, default "app"
provider: "auto", // "auto" | "local" | "aws" | "gcp", env LOG_PROVIDER
level: "info", // env LOG_LEVEL, default "debug" locally / "info" on cloud
defaultMeta: { version: "1.4.0" },
console: true, // write to stdout
redactKeys: [...DEFAULT_REDACT_KEYS, "otp"],
file: { enabled: true, directory: "./logs", maxSize: "20m", maxFiles: "7d" },
aws: {
logGroupName: "/services/orders-api", // set to upload through the CloudWatch API
logStreamName: "instance-1", // default <service>-<hostname>-<pid>
region: "ap-south-1", // default AWS_REGION
retentionInDays: 30,
},
gcp: {
projectId: "my-project", // default GOOGLE_CLOUD_PROJECT, enables trace linking
useCloudLoggingApi: false, // true = upload through the Cloud Logging API
keyFilename: "/secrets/sa.json",
logName: "orders-api",
},
});provider: "auto" detects AWS from AWS_LAMBDA_FUNCTION_NAME / AWS_EXECUTION_ENV / ECS_CONTAINER_METADATA_URI*, and GCP from K_SERVICE / CLOUD_RUN_JOB / FUNCTION_TARGET / GAE_SERVICE. On EKS, GKE or a plain VM, set provider (or LOG_PROVIDER) explicitly.
On Lambda / ECS / Cloud Run you usually don't need the API upload. stdout is already shipped. If you enable it anyway, set
console: falseto avoid duplicate logs.
gcp.useCloudLoggingApineeds valid Google credentials. If they are missing,@google-cloud/logging-winstonthrows an unhandled rejection inside its gRPC client, which crashes the process. traceer cannot catch that error.
Request context
HTTP
app.use(requestLogger(logger, {
buffer: true, // default: collect a request's logs and write them together when it ends
requestIdHeader: "x-request-id", // reused if sent by the caller, echoed on the response
logRequests: true, // one summary log per request
ignorePaths: ["/health"],
}));With buffer: true, a request's logs are written together (in order, with their original timestamps) when the response finishes. Logs written after the response are sent immediately. A request that buffers more than 1000 entries is flushed early. Use buffer: false to write every log as soon as it happens.
Jobs, queues, Lambda handlers
import { withRequestContext } from "@sidhant_001/traceer";
export const handler = async (event, context) =>
withRequestContext(logger, async () => {
logger.info("Processing event", { records: event.Records.length });
// ...
}, { requestId: context.awsRequestId });Child loggers
const paymentsLog = logger.child({ module: "payments" });
paymentsLog.warn("Card declined", { orderId });Errors
try { ... } catch (err) {
logger.error("Payment failed", err); // -> { error: { name, message, stack, cause, code } }
logger.error("Payment failed", { orderId, error: err });
}Development
npm run build # compiles src -> dist with .d.ts files
npm test # builds, then runs the test suite (node:test, no extra dependencies)
npm run test:coverage # same, with a coverage report
npm run example # runs examples/express-app.js
npm publish # runs the tests first (prepublishOnly)Tests live in test/ and run against the compiled dist/, the same code users install. They cover:
- log levels and output formats
- redaction and error serialization
- request context buffering
- the HTTP middleware against a real server
- config and env resolution
- file logging
close()flushing
CI (.github/workflows/ci.yml) runs on every push and pull request. It:
- type-checks and runs the tests on Node 20, 22 and 24
- reports coverage
- fails if anything other than
dist/,README.mdandpackage.jsonwould be published - installs the packed tarball into an empty project as a final smoke test
