Replies: 9 comments 8 replies
-
I understand this proposal is for build-time setup of log formats, but it would also be nice to be able to forward the server-side logs to a third party logging provider, e.g Splunk. We recently had an error in one of our environments where we got the client-side digest, but we could not access the server-side matching log that actually tells you what is wrong, without accessing the pod in Kubernetes (We are running standalone mode.) Hopefully not to unrelated :) |
Beta Was this translation helpful? Give feedback.
-
I wonder what other people think of the proposed methods, as I could give it a try to build something in myself. But I need other people's opinion on this topic, preferably someone from the NextJS team, else my work would be for nothing. I think my preference goes to a separate file named |
Beta Was this translation helpful? Give feedback.
-
For SSR apps, server-side logging shouldn't be an afterthought, imho. It's essential to production application observability. It's a little wild that the best solutions for this currently are inarguably hacks to get around next not providing a way to easily do this. Most frameworks like this just provide some sort of middleware feature, to hook into things and do what you want (one of which, could be logging). I'd be in favor of something extensible like that. Next does have a middleware concept via middleware.js, but it's only supported for edge runtime, which makes me 😞. |
Beta Was this translation helpful? Give feedback.
-
What about having a Then all one would need to do is instantiated their logger of choice, e.g. pino, bunyan, winston, and then assuming the interface is suitable, i.e. implements the relevant methods, we can pass an instance of this to NextJS. |
Beta Was this translation helpful? Give feedback.
-
Been grappling with this all day, for those of us who use alternative hosting outside of Vercel, structured logging would be absolutely fantastic. The only real "solutions" I've seen right now are both incredibly sketchy:
|
Beta Was this translation helpful? Give feedback.
-
This is what we do in if (process.env.NEXT_RUNTIME === 'nodejs') {
console.error = (msg, ...args) => consoleAdapter('error', msg, args);
console.warn = (msg, ...args) => consoleAdapter('warn', msg, args);
console.info = (msg, ...args) => consoleAdapter('info', msg, args);
console.log = (msg, ...args) => consoleAdapter('info', msg, args);
console.debug = (msg, ...args) => consoleAdapter('debug', msg, args);
} It works alright, though there is some weird stuff going on with the edge runtime calling console.log() twice, so it would be better if next supported a custom logger. |
Beta Was this translation helpful? Give feedback.
-
If you want DataDog (or other cloud providers) support along with the logging library you use (eg pino), use my logging library, LogLayer. Here's an example impl: // instrumentation.ts
import { LogLayer, type ILogLayer } from 'loglayer';
import { DataDogTransport } from "@loglayer/transport-datadog";
import { PinoTransport } from "@loglayer/transport-pino";
import pino from "pino";
import { serializeError } from "serialize-error";
function stripAnsiCodes(str: string): string {
// This regex matches all ANSI escape sequences that next.js likes to put in the console logs
return str.replace(
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
"",
);
}
function createConsoleMethod(log: ILogLayer, method: "error" | "info" | "warn" | "debug" | "log") {
let mappedMethod = method;
if (method === "log") {
mappedMethod = "info";
}
return (...args: unknown[]) => {
const data: Record<string, unknown> = {};
let hasData = false;
let error: Error | null = null;
const messages: string[] = [];
for (const arg of args) {
if (arg instanceof Error) {
error = arg;
continue;
}
if (typeof arg === "object" && arg !== null) {
Object.assign(data, arg);
hasData = true;
continue;
}
if (typeof arg === "string") {
messages.push(arg);
}
}
let finalMessage = stripAnsiCodes(messages.join(" ")).trim();
// next.js uses an "x" for the error message when it's an error object
if (finalMessage === "⨯" && error) {
finalMessage = error?.message || "";
}
if (error && hasData && messages.length > 0) {
log.withError(error).withMetadata(data)[mappedMethod](finalMessage);
} else if (error && messages.length > 0) {
log.withError(error)[mappedMethod](finalMessage);
} else if (hasData && messages.length > 0) {
log.withMetadata(data)[mappedMethod](finalMessage);
} else if (error && hasData && messages.length === 0) {
log.withError(error).withMetadata(data)[mappedMethod]("");
} else if (error && messages.length === 0) {
log.errorOnly(error);
} else if (hasData && messages.length === 0) {
log.metadataOnly(data);
} else {
log[mappedMethod](finalMessage);
}
};
}
export async function register() {
const logger = new LogLayer({
errorSerializer: serializeError,
transport: [
new PinoTransport({
logger: pino(),
}),
new DataDogTransport(...),
]
})
if (process.env.NEXT_RUNTIME === "nodejs") {
console.error = createConsoleMethod(logger, "error");
console.log = createConsoleMethod(logger, "log");
console.info = createConsoleMethod(logger, "info");
console.warn = createConsoleMethod(logger, "warn");
console.debug = createConsoleMethod(logger, "debug");
}
} Will output something like:
and also send to DataDog as well. |
Beta Was this translation helpful? Give feedback.
-
Would also be nice to somehow expose the pathname & certain headers (e.g. user-agent, remote addr). So it's easier to understand on which path the error occurs and what an external cause could be |
Beta Was this translation helpful? Give feedback.
-
Im looking into implementing this. Do people care about passing a custom logger for the build phase? Personally I'm only interested in customising the logger at runtime. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Goals
Non-Goals
No response
Background
Websites with a lot of traffic have multiple instances/pods running, which all throw logs regarding errors, readiness & info logs. Going through all of the instances/pods one by one to search for certain logs is inefficient, so people use log collectors like Kibana & Grafana. However, as nextjs logs are not formatted in an easy parsable way (in this case for Kibana it would like a JSON format), it makes it hard to search and filter certain information.
For now there's an solution called next-logger, which basically overwrites the current logger import. This solution is not ideal as it requires a bit of hacking, so a build-in solution would be appreciated.
Proposal
Method 1
With this method, you get a few variables (type & message - which are currently the only 2 variables for the current logging in log.ts) and it's up to the user how they want to log messages. This can be through any logging library or something they build themself.
This logger could also be a seperate file, just like
instrumentation.ts
Method 2
Or a kind of templating style like Spring LogBack? With this method the user is only allowed to format the string, the console.log and interpolation is handled by nextjs.
Note
instrumentation.ts
) could be used.Beta Was this translation helpful? Give feedback.
All reactions