๋ฉ”์ธ ์ฝ˜ํ…์ธ ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ
Sonamu๋Š” LogTape๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์กฐํ™”๋œ ๋กœ๊น…์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. sonamu.config.ts์˜ logging ์˜ต์…˜์œผ๋กœ ๋กœ๊น… ๋™์ž‘์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

LogTape๋ž€?

LogTape๋Š” TypeScript๋ฅผ ์œ„ํ•œ ๊ตฌ์กฐํ™”๋œ ๋กœ๊น… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ๋‹จ์ˆœํ•œ ๋ฌธ์ž์—ด ๋กœ๊ทธ ๋Œ€์‹ , ๊ตฌ์กฐํ™”๋œ ๋ฐ์ดํ„ฐ๋กœ ๋กœ๊ทธ๋ฅผ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.

์ผ๋ฐ˜ ๋กœ๊น… vs ๊ตฌ์กฐํ™”๋œ ๋กœ๊น…

์ผ๋ฐ˜ ๋กœ๊น… (๋ฌธ์ž์—ด):
console.log("User login failed: [email protected], reason=invalid_password");
// โ†’ ๋ถ„์„ํ•˜๋ ค๋ฉด ๋ฌธ์ž์—ด ํŒŒ์‹ฑ ํ•„์š”
๊ตฌ์กฐํ™”๋œ ๋กœ๊น… (LogTape):
logger.error("User login failed", {
  email: "[email protected]",
  reason: "invalid_password",
});
// โ†’ ์ด๋ฏธ ๊ตฌ์กฐํ™”๋œ ๋ฐ์ดํ„ฐ, ๋ฐ”๋กœ ๋ถ„์„ ๊ฐ€๋Šฅ

๊ตฌ์กฐํ™”๋œ ๋กœ๊น…์˜ ์žฅ์ 

  1. ๊ฒ€์ƒ‰/ํ•„ํ„ฐ๋ง ์šฉ์ด - ํŠน์ • ์กฐ๊ฑด์˜ ๋กœ๊ทธ๋งŒ ์‰ฝ๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ์Œ
  2. ๋ถ„์„ ๊ฐ€๋Šฅ - ๋กœ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ์ง‘๊ณ„ํ•˜๊ณ  ํ†ต๊ณ„๋ฅผ ๋‚ผ ์ˆ˜ ์žˆ์Œ
  3. ํƒ€์ž… ์•ˆ์ „ - TypeScript๋กœ ๋กœ๊ทธ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ๊ฒ€์ฆ
  4. ์œ ์—ฐํ•œ ์ถœ๋ ฅ - ๊ฐ™์€ ๋กœ๊ทธ๋ฅผ ์—ฌ๋Ÿฌ ํ˜•์‹(์ฝ˜์†”, ํŒŒ์ผ, ์™ธ๋ถ€ ์„œ๋น„์Šค)์œผ๋กœ ์ถœ๋ ฅ

ํ•ต์‹ฌ ๊ฐœ๋…

1. Sink (์ถœ๋ ฅ ๋Œ€์ƒ)

๋กœ๊ทธ๊ฐ€ ์ตœ์ข…์ ์œผ๋กœ ๊ธฐ๋ก๋˜๋Š” ๊ณณ์ž…๋‹ˆ๋‹ค.
{
  console: getConsoleSink(),        // ํ„ฐ๋ฏธ๋„ ์ถœ๋ ฅ
  file: getFileSink("app.log"),     // ํŒŒ์ผ ์ €์žฅ
  sentry: sentrySink,                // ์™ธ๋ถ€ ์„œ๋น„์Šค
}
ํ•œ ๋กœ๊ทธ๋ฅผ ์—ฌ๋Ÿฌ Sink์— ๋™์‹œ์— ๊ธฐ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
  • ์ฝ˜์†”: ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง
  • ํŒŒ์ผ: ์˜๊ตฌ ๋ณด๊ด€
  • Sentry: ์—๋Ÿฌ ์•Œ๋ฆผ

2. Filter (ํ•„ํ„ฐ๋ง ์กฐ๊ฑด)

์–ด๋–ค ๋กœ๊ทธ๋ฅผ ๊ธฐ๋กํ• ์ง€ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
{
  "api-only": (record) => record.url.startsWith("/api"),
  "errors": (record) => record.level >= "error",
}
Filter ์—†์ด๋Š” ๋ชจ๋“  ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋˜์–ด ์„ฑ๋Šฅ๊ณผ ๊ฐ€๋…์„ฑ์ด ์ €ํ•˜๋ฉ๋‹ˆ๋‹ค.

3. Logger (๋กœ๊ฑฐ ์„ค์ •)

์นดํ…Œ๊ณ ๋ฆฌ, Sink, Filter๋ฅผ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.
{
  category: ["fastify"],           // ์–ด๋–ค ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋กœ๊ทธ์ธ๊ฐ€?
  sinks: ["console", "file"],      // ์–ด๋””์— ๊ธฐ๋กํ• ๊นŒ?
  filters: ["api-only"],           // ์–ด๋–ค ์กฐ๊ฑด์œผ๋กœ ํ•„ํ„ฐ๋งํ• ๊นŒ?
  lowestLevel: "info",             // ์ตœ์†Œ ์–ด๋–ค ๋ ˆ๋ฒจ๋ถ€ํ„ฐ?
}

Sink-Filter-Logger์˜ ๊ด€๊ณ„

์˜ˆ์‹œ:
// 1. Fastify๊ฐ€ ๋กœ๊ทธ ๋ฐœ์ƒ
logger.info("Request completed", { url: "/api/user" });

// 2. Logger ํ™•์ธ: category๊ฐ€ ["fastify"]์ธ๊ฐ€? โœ“
// 3. Filter ํ™•์ธ: url์ด /api๋กœ ์‹œ์ž‘ํ•˜๋Š”๊ฐ€? โœ“
// 4. Sink ์ถœ๋ ฅ: ์ฝ˜์†”๊ณผ ํŒŒ์ผ์— ๊ธฐ๋ก

๋กœ๊ทธ ๋ ˆ๋ฒจ ์„ ํƒ ๊ฐ€์ด๋“œ

๋กœ๊ทธ ๋ ˆ๋ฒจ์€ ๋กœ๊ทธ์˜ ์ค‘์š”๋„๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

๊ฐ ๋ ˆ๋ฒจ์˜ ์˜๋ฏธ์™€ ์‚ฌ์šฉ ์‹œ๊ธฐ

1. debug
  • ์˜๋ฏธ: ์ƒ์„ธํ•œ ๋””๋ฒ„๊น… ์ •๋ณด
  • ์‚ฌ์šฉ ์‹œ๊ธฐ: ๊ฐœ๋ฐœ ์ค‘ ๋ฌธ์ œ ์ถ”์ 
  • ์˜ˆ์‹œ: ํ•จ์ˆ˜ ํ˜ธ์ถœ ์ˆœ์„œ, ๋ณ€์ˆ˜ ๊ฐ’
logger.debug("Processing order", { orderId, items });
2. info (๊ธฐ๋ณธ๊ฐ’)
  • ์˜๋ฏธ: ์ผ๋ฐ˜์ ์ธ ์šด์˜ ์ •๋ณด
  • ์‚ฌ์šฉ ์‹œ๊ธฐ: ์ •์ƒ ๋™์ž‘ ํ™•์ธ
  • ์˜ˆ์‹œ: HTTP ์š”์ฒญ/์‘๋‹ต, ์ž‘์—… ์™„๋ฃŒ
logger.info("User logged in", { userId });
3. warning
  • ์˜๋ฏธ: ๋ฌธ์ œ ๊ฐ€๋Šฅ์„ฑ ๊ฒฝ๊ณ 
  • ์‚ฌ์šฉ ์‹œ๊ธฐ: ์—๋Ÿฌ๋Š” ์•„๋‹ˆ์ง€๋งŒ ์ฃผ์˜ ํ•„์š”
  • ์˜ˆ์‹œ: ๋А๋ฆฐ ์‘๋‹ต, ์žฌ์‹œ๋„ ๋ฐœ์ƒ
logger.warn("Slow query detected", { duration: 5000 });
4. error
  • ์˜๋ฏธ: ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅํ•œ ์—๋Ÿฌ
  • ์‚ฌ์šฉ ์‹œ๊ธฐ: ์˜ˆ์™ธ ์ฒ˜๋ฆฌ, ๋ณต๊ตฌ ๊ฐ€๋Šฅํ•œ ์‹คํŒจ
  • ์˜ˆ์‹œ: ์œ ํšจ์„ฑ ๊ฒ€์ฆ ์‹คํŒจ, API ํ˜ธ์ถœ ์‹คํŒจ
logger.error("Payment failed", { error: "card_declined" });
5. fatal
  • ์˜๋ฏธ: ์น˜๋ช…์  ์˜ค๋ฅ˜ (์„œ๋ฒ„ ์ค‘๋‹จ ์ˆ˜์ค€)
  • ์‚ฌ์šฉ ์‹œ๊ธฐ: ๋ณต๊ตฌ ๋ถˆ๊ฐ€๋Šฅํ•œ ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ
  • ์˜ˆ์‹œ: DB ์—ฐ๊ฒฐ ์‹คํŒจ, ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ
logger.fatal("Database connection lost", { error });

ํ™˜๊ฒฝ๋ณ„ ๋ ˆ๋ฒจ ๊ถŒ์žฅ์‚ฌํ•ญ

ํ™˜๊ฒฝ์ตœ์†Œ ๋ ˆ๋ฒจ์ด์œ 
๊ฐœ๋ฐœdebug๋ชจ๋“  ์ •๋ณด ํ•„์š”
์Šคํ…Œ์ด์ง•info์šด์˜ ์ •๋ณด ํ™•์ธ
ํ”„๋กœ๋•์…˜warning์„ฑ๋Šฅ ๊ณ ๋ ค
๋ ˆ๋ฒจ ๊ณ„์ธต ๊ตฌ์กฐ:
๋‚ฎ์€ ๋ ˆ๋ฒจ์„ ์„ค์ •ํ•˜๋ฉด ๊ทธ๋ณด๋‹ค ๋†’์€ ๋ชจ๋“  ๋ ˆ๋ฒจ์˜ ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ: lowestLevel: "warning"์œผ๋กœ ์„ค์ •ํ•˜๋ฉด warning, error, fatal์ด ๋ชจ๋‘ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

์™œ ์ด๋ ‡๊ฒŒ ์„ค๊ณ„๋˜์—ˆ๋‚˜?

1. ๊ธฐ๋ณธ๊ฐ’์ด ์•ˆ์ „ํ•จ

Sonamu์˜ ๊ธฐ๋ณธ ์„ค์ •:
{
  category: ["fastify"],
  sinks: ["fastify-console"],
  lowestLevel: "info",
  filters: ["fastify-console"],  // /api๋งŒ, healthcheck ์ œ์™ธ
}
์ด์œ :
  • โœ… /api ๊ฒฝ๋กœ๋งŒ ๋กœ๊น… โ†’ ๋ถˆํ•„์š”ํ•œ ์ •์  ํŒŒ์ผ ์š”์ฒญ ์ œ์™ธ
  • โœ… info ๋ ˆ๋ฒจ โ†’ ๋„ˆ๋ฌด ๋งŽ์ง€๋„ ์ ์ง€๋„ ์•Š๊ฒŒ
  • โœ… healthcheck ์ œ์™ธ โ†’ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ์ด ์ƒ์„ฑํ•˜๋Š” ๋ฐ˜๋ณต ์š”์ฒญ ์ œ์™ธ

2. ํ™•์žฅ ๊ฐ€๋Šฅํ•จ

๊ธฐ๋ณธ ์„ค์ • ์œ„์— ์ปค์Šคํ…€ ์„ค์ •์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
logging: {
  // ๊ธฐ๋ณธ ์„ค์ • ์œ ์ง€ํ•˜๋ฉด์„œ
  sinks: {
    errorFile: getFileSink("errors.log"),  // ์ถ”๊ฐ€ Sink
  },
  loggers: [
    // ์ถ”๊ฐ€ Logger (๊ธฐ๋ณธ Fastify ๋กœ๊ฑฐ๋Š” ์œ ์ง€๋จ)
    {
      category: ["app"],
      sinks: ["console"],
      lowestLevel: "debug",
    },
  ],
}

3. ์„ฑ๋Šฅ ์˜ํ–ฅ ์ตœ์†Œํ™”

Filter๋ฅผ ํ†ตํ•ด ๋ถˆํ•„์š”ํ•œ ๋กœ๊ทธ๋ฅผ ์กฐ๊ธฐ์— ์ฐจ๋‹จ:
// โŒ Filter ์—†์Œ: ๋ชจ๋“  ๋กœ๊ทธ ์ฒ˜๋ฆฌ โ†’ ๋А๋ฆผ
logger โ†’ sink (๋ชจ๋“  ๋กœ๊ทธ ์ถœ๋ ฅ)

// โœ… Filter ์žˆ์Œ: ํ•„์š”ํ•œ ๋กœ๊ทธ๋งŒ ์ฒ˜๋ฆฌ โ†’ ๋น ๋ฆ„
logger โ†’ filter (๋ถˆํ•„์š”ํ•œ ๋กœ๊ทธ ์ฐจ๋‹จ) โ†’ sink (ํ•„์š”ํ•œ ๋กœ๊ทธ๋งŒ ์ถœ๋ ฅ)

๊ธฐ๋ณธ ์„ค์ •

logging ์˜ต์…˜์„ ์ƒ๋žตํ•˜๋ฉด ๊ธฐ๋ณธ ์„ค์ •์ด ์ž๋™ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.
import { defineConfig } from "sonamu";

export default defineConfig({
  // logging ์ƒ๋žต ์‹œ ๊ธฐ๋ณธ๊ฐ’ ์‚ฌ์šฉ
  server: {
    // ...
  },
});
๊ธฐ๋ณธ ๋™์ž‘:
  • Fastify ์š”์ฒญ/์‘๋‹ต์„ ์ฝ˜์†”์— ๋กœ๊น…
  • /api/* ๊ฒฝ๋กœ๋งŒ ๋กœ๊น… (healthcheck ์ œ์™ธ)
  • ์˜ˆ์œ ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅ (ํƒ€์ž„์Šคํƒฌํ”„, ์นดํ…Œ๊ณ ๋ฆฌ, ๋ ˆ๋ฒจ)

logging ์˜ต์…˜

type SonamuLoggingOptions<TSinkId extends string, TFilterId extends string> = {
  fastifyCategory?: readonly string[];
  sinks?: Record<TSinkId, Sink>;
  filters?: Record<TFilterId, FilterLike>;
  loggers?: LoggerConfig<TSinkId, TFilterId>[];
};

๋กœ๊น… ๋น„ํ™œ์„ฑํ™”

export default defineConfig({
  logging: false,  // ๋ชจ๋“  ๋กœ๊น… ๋น„ํ™œ์„ฑํ™”
  
  server: {
    // ...
  },
});

fastifyCategory

Fastify HTTP ๋กœ๊น…์— ์‚ฌ์šฉํ•  ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ํƒ€์ž…: readonly string[] ๊ธฐ๋ณธ๊ฐ’: ["fastify"]
export default defineConfig({
  logging: {
    fastifyCategory: ["myapp", "http"],  // ์ปค์Šคํ…€ ์นดํ…Œ๊ณ ๋ฆฌ
  },
  
  server: {
    // ...
  },
});
์นดํ…Œ๊ณ ๋ฆฌ ํ˜•์‹: ["a", "b", "c"] ๋ฐฐ์—ด๋กœ ๊ณ„์ธต ๊ตฌ์กฐ ํ‘œํ˜„ ๋กœ๊ทธ ์ถœ๋ ฅ ์˜ˆ์‹œ:
[2025-01-09 12:34:56] [myapp.http] INFO: [GET:200] /api/user/list - Request completed

sinks

๋กœ๊ทธ๋ฅผ ์ถœ๋ ฅํ•  ๋Œ€์ƒ(sink)์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ํƒ€์ž…: Record<string, Sink>
import { defineConfig } from "sonamu";
import { getConsoleSink, getFileSink } from "@logtape/logtape";

export default defineConfig({
  logging: {
    sinks: {
      console: getConsoleSink(),
      file: getFileSink("logs/app.log"),
    },
  },
  
  server: {
    // ...
  },
});
"fastify-console" ์ด๋ฆ„์€ Sonamu๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ๊ธฐ๋ณธ sink์ž…๋‹ˆ๋‹ค. ์ด ์ด๋ฆ„์œผ๋กœ ์ถ”๊ฐ€ํ•˜๋ฉด ๊ธฐ๋ณธ sink๋ฅผ ๋ฎ์–ด์”๋‹ˆ๋‹ค.

filters

๋กœ๊ทธ๋ฅผ ์„ ํƒ์ ์œผ๋กœ ํ•„ํ„ฐ๋งํ•˜๋Š” ์กฐ๊ฑด์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ํƒ€์ž…: Record<string, FilterLike>
import { defineConfig } from "sonamu";
import type { LogRecord } from "@logtape/logtape";

export default defineConfig({
  logging: {
    filters: {
      "errors-only": (record: LogRecord) => record.level >= "error",
    },
  },
  
  server: {
    // ...
  },
});
"fastify-console" ์ด๋ฆ„์€ Sonamu๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ๊ธฐ๋ณธ filter์ž…๋‹ˆ๋‹ค. ์ด ์ด๋ฆ„์œผ๋กœ ์ถ”๊ฐ€ํ•˜๋ฉด ๊ธฐ๋ณธ filter๋ฅผ ๋ฎ์–ด์”๋‹ˆ๋‹ค.

loggers

์นดํ…Œ๊ณ ๋ฆฌ๋ณ„๋กœ ์–ด๋–ค sink์™€ filter๋ฅผ ์‚ฌ์šฉํ• ์ง€ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ํƒ€์ž…: LoggerConfig[]
import { defineConfig } from "sonamu";
import { getConsoleSink } from "@logtape/logtape";

export default defineConfig({
  logging: {
    sinks: {
      console: getConsoleSink(),
    },
    
    loggers: [
      {
        category: ["fastify"],
        sinks: ["console"],
        lowestLevel: "info",
      },
    ],
  },
  
  server: {
    // ...
  },
});
fastifyCategory์— ์„ค์ •๋œ ์นดํ…Œ๊ณ ๋ฆฌ์˜ logger ์„ค์ •์ด ์žˆ์œผ๋ฉด, Sonamu๋Š” ๊ธฐ๋ณธ logger๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ์˜ˆ์‹œ

์ตœ์†Œ ์„ค์ • (๊ธฐ๋ณธ๊ฐ’ ์‚ฌ์šฉ)

import { defineConfig } from "sonamu";

export default defineConfig({
  server: {
    listen: { port: 1028 },
  },
});

๋กœ๊น… ๋น„ํ™œ์„ฑํ™”

import { defineConfig } from "sonamu";

export default defineConfig({
  logging: false,
  
  server: {
    listen: { port: 1028 },
  },
});

์ปค์Šคํ…€ ์นดํ…Œ๊ณ ๋ฆฌ

import { defineConfig } from "sonamu";

export default defineConfig({
  logging: {
    fastifyCategory: ["app", "server", "http"],
  },
  
  server: {
    listen: { port: 1028 },
  },
});

๊ธฐ๋ณธ ๋™์ž‘ ์ƒ์„ธ

Sonamu๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž๋™์œผ๋กœ ๋กœ๊น…์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค:

1. Fastify Sink ์ž๋™ ์ƒ์„ฑ

// ์ž๋™ ์ƒ์„ฑ๋˜๋Š” "fastify-console" sink
{
  type: "console",
  formatter: prettyFormatter({
    timestamp: "time",
    categoryWidth: 20,
    categoryTruncate: "middle",
  }),
}
ํŠน์ง•:
  • HTTP ๋ฉ”์„œ๋“œ์™€ ์‘๋‹ต ์ฝ”๋“œ ํ‘œ์‹œ: [GET:200]
  • ์š”์ฒญ URL ํ‘œ์‹œ: /api/user/list
  • ์ƒ‰์ƒ์œผ๋กœ ๋ ˆ๋ฒจ ๊ตฌ๋ถ„

2. Fastify Filter ์ž๋™ ์ƒ์„ฑ

// ์ž๋™ ์ƒ์„ฑ๋˜๋Š” "fastify-console" filter
(record) => {
  // /api๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ฒฝ๋กœ๋งŒ, healthcheck ์ œ์™ธ
  return request.url.startsWith("/api") && 
         request.url !== "/api/healthcheck";
}

3. Logger ์ž๋™ ์ƒ์„ฑ

// ์ž๋™ ์ƒ์„ฑ๋˜๋Š” logger
{
  category: ["fastify"],  // ๋˜๋Š” ์‚ฌ์šฉ์ž ์ง€์ • fastifyCategory
  sinks: ["fastify-console"],
  lowestLevel: "info",
  filters: ["fastify-console"],
}

4. Meta Logger ๋น„ํ™œ์„ฑํ™”

// ์ž๋™ ์ถ”๊ฐ€๋˜๋Š” logger
{
  category: ["logtape", "meta"],
  lowestLevel: "fatal",  // LogTape ๋‚ด๋ถ€ ๋กœ๊ทธ ์ˆจ๊น€
}

๋กœ๊ทธ ๋ ˆ๋ฒจ

LogTape์˜ ๋กœ๊ทธ ๋ ˆ๋ฒจ (๋‚ฎ์€ ์ˆœ์„œ):
  1. debug - ์ƒ์„ธํ•œ ๋””๋ฒ„๊น… ์ •๋ณด
  2. info - ์ผ๋ฐ˜ ์ •๋ณด (๊ธฐ๋ณธ๊ฐ’)
  3. warning - ๊ฒฝ๊ณ 
  4. error - ์—๋Ÿฌ
  5. fatal - ์น˜๋ช…์  ์˜ค๋ฅ˜

์‹ค์ „ ์˜ˆ์‹œ

import { defineConfig } from "sonamu";

export default defineConfig({
  logging: {
    fastifyCategory: ["fastify"],
    loggers: [
      {
        category: ["fastify"],
        sinks: ["fastify-console"],
        lowestLevel: "debug",  // ๋ชจ๋“  ๋กœ๊ทธ ํ™•์ธ
        filters: ["fastify-console"],
      },
    ],
  },
  
  server: {
    listen: { port: 1028 },
  },
});
ํŠน์ง•:
  • debug ๋ ˆ๋ฒจ๋กœ ๋ชจ๋“  ์ƒ์„ธ ์ •๋ณด ํ™•์ธ
  • ์ฝ˜์†” ์ถœ๋ ฅ์œผ๋กœ ์‹ค์‹œ๊ฐ„ ํ™•์ธ
  • ๋น ๋ฅธ ํ”ผ๋“œ๋ฐฑ ๋ฃจํ”„

ํŒŒ์ผ ๋กœ๊น… ์ถ”๊ฐ€

import { defineConfig } from "sonamu";
import { getConsoleSink, getFileSink } from "@logtape/logtape";

export default defineConfig({
  logging: {
    sinks: {
      console: getConsoleSink(),
      file: getFileSink("logs/app.log"),
      errorFile: getFileSink("logs/errors.log"),
    },
    
    filters: {
      "errors-only": (record) => record.level >= "error",
    },
    
    loggers: [
      // ์ฝ˜์†”: ๋ชจ๋“  ๋กœ๊ทธ
      {
        category: ["fastify"],
        sinks: ["console"],
        lowestLevel: "info",
      },
      // ํŒŒ์ผ: ๋ชจ๋“  ๋กœ๊ทธ
      {
        category: ["fastify"],
        sinks: ["file"],
        lowestLevel: "info",
      },
      // ์—๋Ÿฌ ํŒŒ์ผ: ์—๋Ÿฌ๋งŒ
      {
        category: ["fastify"],
        sinks: ["errorFile"],
        filters: ["errors-only"],
        lowestLevel: "error",
      },
    ],
  },
  
  server: {
    listen: { port: 1028 },
  },
});

ํ”„๋กœ๋•์…˜ ์„ค์ •

import { defineConfig } from "sonamu";
import { getConsoleSink, getFileSink } from "@logtape/logtape";

export default defineConfig({
  logging: {
    sinks: {
      console: getConsoleSink(),
      errorLog: getFileSink("logs/errors.log"),
    },
    
    filters: {
      "errors": (record) => record.level >= "error",
    },
    
    loggers: [
      // ์ฝ˜์†”: warning ์ด์ƒ๋งŒ
      {
        category: ["fastify"],
        sinks: ["console"],
        lowestLevel: "warning",
      },
      // ํŒŒ์ผ: ์—๋Ÿฌ๋งŒ
      {
        category: ["fastify"],
        sinks: ["errorLog"],
        filters: ["errors"],
        lowestLevel: "error",
      },
    ],
  },
  
  server: {
    listen: { port: 1028 },
  },
});

Fastify ๋กœ๊น… ์ž๋™ ํ†ตํ•ฉ

Sonamu๋Š” @logtape/fastify ํŒจํ‚ค์ง€๋กœ Fastify ๋กœ๊น…์„ ์ž๋™ ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค.
// Sonamu ๋‚ด๋ถ€์—์„œ ์ž๋™์œผ๋กœ ์ˆ˜ํ–‰
import { getLogTapeFastifyLogger } from "@logtape/fastify";

const server = fastify({
  logger: getLogTapeFastifyLogger({
    category: config.logging?.fastifyCategory ?? ["fastify"],
  }),
});
์ž๋™์œผ๋กœ ๋กœ๊น…๋˜๋Š” ์ •๋ณด:
  • HTTP ์š”์ฒญ (method, URL)
  • ์‘๋‹ต ์ฝ”๋“œ
  • ์‘๋‹ต ์‹œ๊ฐ„
  • ์—๋Ÿฌ ์ŠคํƒํŠธ๋ ˆ์ด์Šค

์ฃผ์˜์‚ฌํ•ญ

1. ๊ธฐ๋ณธ sink/filter ๋ฎ์–ด์“ฐ๊ธฐ

// โŒ ๋‚˜์œ ์˜ˆ: ๊ธฐ๋ณธ ์„ค์ • ์†์‹ค
logging: {
  sinks: {
    "fastify-console": getConsoleSink(),  // ๊ธฐ๋ณธ formatter ์†์‹ค
  },
}

// โœ… ์ข‹์€ ์˜ˆ: ๋‹ค๋ฅธ ์ด๋ฆ„ ์‚ฌ์šฉ
logging: {
  sinks: {
    "my-console": getConsoleSink(),
  },
}

2. ๋กœ๊น… ๋น„ํ™œ์„ฑํ™” ์‹œ logger ์˜ต์…˜

// โŒ ๋‚˜์œ ์˜ˆ: ์ถฉ๋Œ
logging: false,
server: {
  fastify: {
    logger: true,  // logging: false์™€ ์ถฉ๋Œ
  },
}

// โœ… ์ข‹์€ ์˜ˆ: ์ผ๊ด€์„ฑ ์œ ์ง€
logging: false,
server: {
  fastify: {
    // logger ์„ค์ • ์•ˆ ํ•จ (๊ธฐ๋ณธ๊ฐ’ ์‚ฌ์šฉ)
  },
}

3. ์นดํ…Œ๊ณ ๋ฆฌ ์ผ๊ด€์„ฑ

// โœ… ์ข‹์€ ์˜ˆ: ์ผ๊ด€๋œ ์นดํ…Œ๊ณ ๋ฆฌ
fastifyCategory: ["app", "http"]

// โŒ ๋‚˜์œ ์˜ˆ: ์˜๋ฏธ ์—†๋Š” ์นดํ…Œ๊ณ ๋ฆฌ
fastifyCategory: ["a", "b", "c"]

๋‹ค์Œ ๋‹จ๊ณ„