# How to Set Up Structured Logging with xlog in Fabrica-Kit

> Learn to set up structured logging with xlog in fabrica-kit. Initialize your global JSON logger in main.go to automatically inject timestamps, service metadata, and trace IDs into every log entry.

- Repository: [Pantheon/fabrica-kit](https://github.com/go-pantheon/fabrica-kit)
- Tags: how-to-guide
- Published: 2026-03-02

---

**Call `xlog.Init("zap", "info", profile, color, svcName, svcVer, nodeName)` early in your [`main.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/main.go) to initialize a global JSON logger that automatically injects timestamps, service metadata, and distributed trace IDs into every log entry.**

Setting up structured logging with xlog in fabrica-kit provides a production-ready observability foundation for Go microservices. The `xlog` package acts as a logging façade that integrates Kratos' logger abstraction with Uber's high-performance Zap encoder, outputting machine-readable JSON logs enriched with contextual fields like service name, version, and trace correlation IDs.

## Core Architecture of xlog

The `xlog` package in [[`xlog/log.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go)](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go) serves as the central configuration point. It wraps Kratos' `log.Logger` interface and, when the `"zap"` type is specified, configures a Zap JSON encoder that writes to stdout. This design ensures that once initialized, every log entry carries a consistent schema suitable for ingestion by ELK, Loki, or cloud-native logging platforms.

## Configuring the Global Logger

### The Init Function

The primary entry point is `xlog.Init`, defined in [`xlog/log.go:22-43`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go#L22-L43). This function selects the concrete logger implementation and decorates it with permanent fields that appear in every subsequent log entry.

```go
func Init(logType, logLevel, profile, color, name, version, nodeName string) log.Logger

```

### Supported Log Types and Levels

As implemented in [`xlog/log.go:25-30`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go#L25-L30), the system supports two logger types:

- **`"zap"`** – Activates the Zap JSON encoder for structured output.
- **Any other value** – Falls back to the default Kratos logger (plain text).

Log level handling occurs in [`xlog/log.go:60-71`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go#L60-L71). The function accepts `debug`, `info`, `warn`, and `error` as valid strings, mapping them to Zap's native levels. Unrecognized levels default to `Info` to prevent startup failures.

### Automatic Structured Fields

The initialization process injects a standard set of fields into every log entry, as defined in [`xlog/log.go:32-42`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go#L32-L42):

- `ts` – Timestamp in `time.DateTime` format
- `profile` – Deployment profile (e.g., `prod`, `dev`)
- `color` – Optional UI color tag
- `caller` – File and line number of the log call
- `svc` – Service name
- `sver` – Service version
- `node` – Node name
- `trace` – Kratos trace ID
- `span` – Kratos span ID

The Zap JSON encoder configuration in [`xlog/log.go:48-78`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go#L48-L78) uses `MsgKey = "msg"` for the log message, lower-cases level names, formats time as ISO-8601, and writes output to `stdout`.

## Implementation Examples

### Basic Initialization in main.go

Call `xlog.Init` before any other logging operations to ensure the global logger is configured. This example demonstrates the typical bootstrap pattern:

```go
package main

import (
	"log"

	"github.com/go-pantheon/fabrica-kit/xlog"
)

func main() {
	const (
		logType  = "zap"      // Use "zap" for JSON output
		logLevel = "info"     // debug | info | warn | error
		profile  = "prod"
		color    = "blue"
		svcName  = "order-service"
		svcVer   = "v1.2.3"
		nodeName = "node-01"
	)

	// Initialize the structured logger and register it globally
	logger := xlog.Init(logType, logLevel, profile, color, svcName, svcVer, nodeName)
	_ = logger

	// All subsequent log calls include structured fields automatically
	log.Info("service started")
}

```

### Using the Logger in HTTP Handlers

After initialization, use the standard Kratos `log` package. The global logger automatically enriches entries with the fields configured during `Init`, including trace IDs from the request context:

```go
package handler

import (
	"context"
	
	"github.com/go-kratos/kratos/v2/log"
)

func (h *Handler) GetUser(ctx context.Context, id string) (*User, error) {
	log.Infof("handling GetUser request", "userID", id)

	user, err := h.repo.Find(id)
	if err != nil {
		log.Errorf("failed to fetch user", "userID", id, "error", err)
		return nil, err
	}
	
	log.Debugf("user payload retrieved", "user", user)
	return user, nil
}

```

This produces JSON output similar to:

```json
{
  "ts": "2026-03-02 15:04:05",
  "profile": "prod",
  "color": "blue",
  "caller": "handler/user.go:42",
  "svc": "order-service",
  "sver": "v1.2.3",
  "node": "node-01",
  "trace": "0a1b2c3d4e5f",
  "span": "6f7e8d9c0b1a",
  "level": "info",
  "msg": "handling GetUser request",
  "userID": "12345"
}

```

### Component-Level Logger Overrides

For subsystems requiring additional context, create a child logger using `log.With`. Note that the underlying log level remains controlled by the global `Init` configuration:

```go
subLogger := log.With(log.GetLogger(),
	"component", "payment",
	"region", "us-east-1",
)

subLogger.Infof("payment processor initialized")

```

## Key Source Files

| File | Role | Location |
|------|------|----------|
| [`xlog/log.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go) | Core implementation of `Init`, Zap encoder configuration, and field injection | [[`xlog/log.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go)](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go) |
| [`xcontext/context.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/xcontext/context.go) | Request-scoped context utilities for trace propagation | [[`xcontext/context.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/xcontext/context.go)](https://github.com/go-pantheon/fabrica-kit/blob/main/xcontext/context.go) |
| [`trace/trace.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/trace.go) | Kratos tracing integration providing `trace` and `span` IDs to the logger | [[`trace/trace.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/trace.go)](https://github.com/go-pantheon/fabrica-kit/blob/main/trace/trace.go) |

## Summary

- **xlog** provides a structured logging façade in fabrica-kit that bridges Kratos and Zap for high-performance JSON output.
- Initialize the logger via `xlog.Init` in [`xlog/log.go:22-43`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go#L22-L43) with parameters for log type (`"zap"`), level (`debug|info|warn|error`), and service metadata.
- Every log entry automatically includes fields for `ts`, `profile`, `caller`, `svc`, `sver`, `node`, `trace`, and `span` as defined in [`xlog/log.go:32-42`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go#L32-L42).
- The Zap encoder writes newline-delimited JSON to `stdout`, making logs immediately compatible with aggregation platforms like ELK, Loki, or cloud-native logging services.

## Frequently Asked Questions

### What log levels does xlog support?

The `xlog.Init` function accepts `debug`, `info`, `warn`, and `error` as valid level strings. As implemented in [`xlog/log.go:60-71`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go#L60-L71), these are mapped to Zap's native levels. If an unrecognized string is provided, the system defaults to `Info` to prevent startup failures.

### How do I switch from JSON to plain text logging?

Change the `logType` parameter in your `xlog.Init` call from `"zap"` to any other value. According to the logic in [`xlog/log.go:25-30`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go#L25-L30), only the exact string `"zap"` triggers the JSON encoder; all other values fall back to the default Kratos logger, which produces human-readable plain text output.

### Can I add custom fields to every log entry?

Yes, but you must do so at initialization or by creating child loggers. The `xlog.Init` function in [`xlog/log.go:32-42`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go#L32-L42) hardcodes the standard fields like `svc`, `sver`, and `node`. For dynamic fields, use `log.With(log.GetLogger(), "key", value)` to create a contextual logger that carries additional key-value pairs for specific components or request scopes.

### Where does xlog write its output?

By default, the Zap encoder configured in [`xlog/log.go:48-78`](https://github.com/go-pantheon/fabrica-kit/blob/main/xlog/log.go#L48-L78) writes all output to **stdout** using a JSON encoder. The encoder uses ISO-8601 timestamps, lower-cased level names, and the message key `"msg"`. This design ensures compatibility with modern log aggregation systems like ELK, Loki, or cloud-native logging services that expect newline-delimited JSON streams.