npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@certicraft/logging

v1.2.1

Published

This package is used to log and debug data to Google Cloud Platform

Downloads

351

Readme

@certicraft/logging

The standardised logger used across CertiCraft's backend services. Same shape, same format, same env knobs — whether you're running s2sWebApi, the message queue, or anything else that lands in our infra.

Why this package exists. Logging started as s2sWebApi/src/libraries_v2/logging inside the s2sBackend monorepo. When the message queue was carved out into its own service, copy-pasting the lib would have meant two logging shapes drifting apart over time. Instead it became this package — one source of truth for how logs look in production (GCP Cloud Logging) and how they read locally during development.


Install

npm install @certicraft/logging

Quick start

import { CloudLogger } from '@certicraft/logging';

// Once, at process startup:
CloudLogger.setEnabled(true);

// Inside your HTTP middleware (per request):
CloudLogger.setLoggerName(process.env.FUNCTION_NAME ?? 'unknown');
CloudLogger.setLabels({ companyId, userId });
CloudLogger.setHttpLabels({
  requestMethod: req.method,
  requestPath:   req.path,
  requestUrl:    req.url,
  remoteIp:      req.ip,
  requestHeaders: req.headers,
});

// Anywhere in the request flow:
CloudLogger.info({ requestId }, 'Request received');
CloudLogger.error({ error, description: 'Lookup failed' });

That's the whole surface for the common case. The logger is a process-singleton — no new, no per-request instances. Context set via setLabels/setHttpLabels is injected into every subsequent log call until the next set.

API surface

Logging

| Call | Use | |---|---| | CloudLogger.info(payload, msg?) | Routine event | | CloudLogger.warn(payload, msg?) | Notable but non-failure | | CloudLogger.error(payload, msg?) | Failure | | CloudLogger.debug(payload, msg?) | Verbose, only emitted at debug level |

payload is a plain object — companyId, userId, requestId, error, err, description, message, plus anything domain-specific. msg is the optional human-readable header string.

You can also pass a bare string (CloudLogger.info('something happened')) — it becomes the message with no payload.

Setup / context

| Call | Use | |---|---| | CloudLogger.setEnabled(true) | Required once at startup. Until called, the logger is silent. | | CloudLogger.setLoggerName(name) | GCP logName / function name. Usually FUNCTION_NAME. | | CloudLogger.setLabels({...}) | Top-level labels merged into every record (e.g. companyId, userId). | | CloudLogger.setHttpLabels({...}) | HTTP request context (method, path, url, ip, headers, latency). | | CloudLogger.setContext({...}) | GCP serviceContext for error reporting. |

Timing / instrumentation

A lightweight time-series API for ad-hoc performance checks. Treat this as a stopgap — when we standardise on OpenTelemetry the right move will be @opentelemetry/api spans, not these calls.

| Call | Use | |---|---| | CloudLogger.time(label) | Start a labelled timer | | CloudLogger.timeLog(label, ...args) | Mark an intermediate point with optional args | | CloudLogger.timeEnd(label) | Stop, log the duration and all the marked points | | CloudLogger.stopwatchStart(label) / stopwatchStop(label) | Cumulative stopwatch — start/stop pairs accumulate across calls | | CloudLogger.stopwatchGetInfo(label) | Log the running total + call count + average | | CloudLogger.flush() | Emit a summary of all active timers + stopwatches |

Serialisation helpers

| Call | Use | |---|---| | getRequestAsLogEntryFields(req) | Turns an Express Request into {requestHeaders, requestPath, requestQuery} with authorization redacted | | convertFSAToLogEntryField(fsa) | Safely serialises a Flux Standard Action (CQRS command) for logging |

Production vs local behaviour

The same code emits two completely different formats depending on where it runs.

Production (NODE_ENV=production)

Records ship to Google Cloud Logging via gcpLogOptions (pino-cloud-logging). Output looks like:

{"severity":"INFO","time":1780861800095,"logging.googleapis.com/labels":{"companyId":"X"},"httpRequest":{"requestMethod":"GET","requestPath":"/"},"message":"hello"}

Local (firebase emulator or LOG_LOCAL=true)

Records render through a custom in-process destination — colourised single line per record, automatic dedupe for storms, automatic request-route tag pulled from setHttpLabels:

[19:50:00.105]: INFO GET /api/v1/getViewModel "Request received"
[19:50:00.106]: INFO GET /api/v1/getViewModel [HTTP_CLIENT] "Calling upstream"  req=out-1
[19:50:00.107]: ERROR GET /api/v1/getViewModel "Falling back to default tenant config"
  ✖ tenant config not found (NOT_FOUND)

For deeper local-mode recipes (silencing emulator chatter, regex pitfalls, troubleshooting), see local-logging.md.

Environment variables

The logger reads these from process.env. Names prefixed with LOG_ are yours to tune; the rest are auto-detected from the host (firebase emulator, test runners, Node) and rarely need to be set explicitly.

Tunable

| Var | Default | What it does | |---|---|---| | LOG_LOCAL | unset | true → force pretty local mode on. false → force off. Otherwise auto-detected from emulator env vars. | | LOG_LEVEL | info (local) | Pino level: trace / debug / info / warn / error / fatal / silent. Only honoured in local mode — production level is controlled by gcpLogOptions. | | LOG_MUTE_SOURCES | empty | Comma-separated. Drops records whose source or label field matches. e.g. redis-client-error-handler,HTTP_CLIENT | | LOG_SILENCE_PATTERNS | empty | Comma-separated regex patterns. Drops records whose msg matches any. e.g. ^Request completed in,not found in the cache |

A typical .env.local looks like:

# Optional — bump only when you need it
# LOG_LEVEL=debug

# Drop high-volume noise
LOG_SILENCE_PATTERNS=^Request completed in,not found in the cache

# Mute whole categories
LOG_MUTE_SOURCES=redis-client-error-handler

Auto-managed by the library locally

The library sets these at startup only when running locally and only when they aren't already defined — so a value you set in your shell wins.

| Var | Default the lib sets | Effect | |---|---|---| | GRPC_VERBOSITY | NONE | Silences gRPC's own stdout chatter (Firestore uses gRPC heavily) | | GRPC_TRACE | '' | Same, for the trace verbosity | | FIRESTORE_LOG_LEVEL | error | Silences firebase-admin's Firestore logger |

Host-detection (rarely set manually)

These are usually set by the host environment; the logger reads them to decide which code path to take.

| Var | Set by | Effect | |---|---|---| | NODE_ENV | your deploy | production → cloud path; anything else → local-path candidate | | FUNCTIONS_EMULATOR | firebase emulator | Set → local pretty mode activates | | FIRESTORE_EMULATOR_HOST | firebase emulator | Set → local pretty mode activates | | FIREBASE_EMULATOR_HUB | firebase emulator | Set → local pretty mode activates | | JEST_WORKER_ID | jest | Set → logger is silent during tests | | VITEST_WORKER_ID | vitest | Set → logger is silent during tests | | FUNCTION_NAME | GCP cloud functions | Convenient setLoggerName() default (see quick start) |

Versioning & changes

See CHANGELOG.md for the version history. Outstanding work is tracked in tasks.md.

Safety guarantees

The local-rendering code is fully gated — it is structurally impossible for it to execute when NODE_ENV=production. This is asserted by src/__tests__/productionInvariants.unit.test.ts, which fails CI if anyone accidentally lets local code leak into the production path.