Secure logger in Go to avoid output sensitive data in log

zlog Go Reference Vulnerability scan Unit test Security Scan

A main distinct feature of zlog is secure logging that avoid to output secret/sensitive values to log. The feature reduce risk to store secret values (API token, password and such things) and sensitive data like PII (Personal Identifiable Information) such as address, phone number, email address and etc into logging storage.

zlog also has major logger features: contextual logging, leveled logging, structured message, show stacktrace of error. See following usage for mote detail.

Usage

Basic example

import "github.com/m-mizutani/zlog"

type myRecord struct {
	Name  string
	EMail string
}

func main() {
	record := myRecord{
		Name:  "mizutani",
		EMail: "[email protected]",
	}

	logger := zlog.New()
	logger.With("record", record).Info("hello my logger")
}

zlog.New() creates a new logger with default settings (console formatter).

example

Contextual logging

Logger.With(key string, value interface{}) method allows contextual logging that output not only message but also related variables. The method saves a pair of key and value and output it by pretty printing (powered by k0kubun/pp).

Filter sensitive data

By specified value

This function is designed to hide limited and predetermined secret values, such as API tokens that the application itself uses to call external services.

const issuedToken = "abcd1234"
authHeader := "Authorization: Bearer " + issuedToken

logger := newExampleLogger()
logger.Filters = []zlog.Filter{
	filter.Value(issuedToken),
}
logger.With("auth", authHeader).Info("send header")
// Output:  [info] send header
// "auth" => "Authorization: Bearer [filtered]"

By field name

This filter hides the secret value if it matches the field name of the specified structure.

type myRecord struct {
	ID    string
	EMail string
}
record := myRecord{
	ID:    "m-mizutani",
	EMail: "[email protected]",
}

logger.Filters = []zlog.Filter{
	filter.Field("EMail"),
}
logger.With("record", record).Info("Got record")
// Output:  [info] Got record
// "record" => zlog_test.myRecord{
//   ID:    "m-mizutani",
//   EMail: "[filtered]",
// }

By custom type

You can define a type that you want to keep secret, and then specify it in a Filter to prevent it from being displayed. The advantage of this method is that copying a value from a custom type to the original type requires a cast, making it easier for the developer to notice unintentional copying. (Of course, this is not a perfect solution because you can still copy by casting.)

This method may be useful for use cases where you need to use secret values between multiple structures.

type password string
type myRecord struct {
	ID    string
	EMail password
}
record := myRecord{
	ID:    "m-mizutani",
	EMail: "abcd1234",
}

logger.Filters = []zlog.Filter{
	filter.Type(password("")),
}
logger.With("record", record).Info("Got record")
// Output:  [info] Got record
// "record" => zlog_test.myRecord{
//   ID:    "m-mizutani",
//   EMail: "[filtered]",
// }

By struct tag

type myRecord struct {
	ID    string
	EMail string `zlog:"secure"`
}
record := myRecord{
	ID:    "m-mizutani",
	EMail: "[email protected]",
}

logger.Filters = []zlog.Filter{
	filter.Tag(),
}
logger.With("record", record).Info("Got record")
// Output:  [info] Got record
// "record" => zlog_test.myRecord{
//   ID:    "m-mizutani",
//   EMail: "[filtered]",
// }

By data pattern (e.g. personal information)

This is an experimental effort and not a very reliable method, but it may have some value. It is a way to detect and hide personal information that should not be output based on a predefined pattern, like many DLP (Data Leakage Protection) solutions.

In the following example, we use a filter that we wrote to detect Japanese phone numbers. The content is just a regular expression. Currently zlog does not have as many patterns as the existing DLP solutions, and the patterns are not accurate enough. However we hope to expand it in the future if necessary.

type myRecord struct {
	ID    string
	Phone string
}
record := myRecord{
	ID:    "m-mizutani",
	Phone: "090-0000-0000",
}

logger.Filters = []zlog.Filter{
	filter.PhoneNumber(),
}
logger.With("record", record).Info("Got record")
// Output:  [info] Got record
// "record" => zlog_test.myRecord{
//   ID:    "m-mizutani",
//   Phone: "[filtered]",
// }

Customize Log output format

zlog has Emitter that is interface to output log event. A default emitter is Writer that has Formatter to format log message, values and error information and io.Writer to output formatted log data.

Change io.Writer

For example, change output to standard error.

logger.Emitter = zlog.NewWriterWith(zlog.NewConsoleFormatter(), os.Stderr)
logger.Info("output to stderr")

Change formatter

For example, use JsonFormatter to output structured json.

logger.Emitter = zlog.NewWriterWith(zlog.NewJsonFormatter(), os.Stdout)
logger.Info("output as json format")
// Output: {"timestamp":"2021-10-02T14:58:11.791258","level":"info","msg":"output as json format","values":null}

Use original emitter

You can use your original Emitter that has Emit(*zlog.Event) error method.

type myEmitter struct {
	seq int
}

func (x *myEmitter) Emit(ev *zlog.Event) error {
	x.seq++
	prefix := []string{"\(^o^)/", "(´・ω・`)", "(・∀・)"}
	fmt.Println(prefix[x.seq%3], ev.Msg)
	return nil
}

func ExampleEmitter() {
	logger := zlog.New()
	logger.Emitter = &myEmitter{}

	logger.Info("waiwai")
	logger.Info("heyhey")
	// Output:
	// \(^o^)/ waiwai
	// (´・ω・`) heyhey
}

Leveled Logging

zlog allows for logging at the following levels.

  • trace (zlog.LevelTrace)
  • debug (zlog.LevelDebug)
  • info (zlog.LevelInfo)
  • warn (zlog.LevelWarn)
  • error (zlog.LevelError)

Log level can be changed by modifying Logger.Level or calling Logger.SetLogLevel() method.

Modifying Logger.Level directly:

	logger.Level = zlog.LevelInfo
	logger.Debug("debugging")
	logger.Info("information")
	// Output: [info] information

Using SetLogLevel() method. Log level is case insensitive.

	logger.SetLogLevel("InFo")

	logger.Debug("debugging")
	logger.Info("information")
	// Output: [info] information

Error handling

Logger.Err(err error) outputs not only error message but also stack trace and error related values.

Output stack trace

Support below error packages.

func crash() error {
	return errors.New("oops")
}

func main() {
	logger := zlog.New()
	if err := crash(); err != nil {
		logger.Err(err).Error("failed")
	}
}

// Output:
// [error] failed
//
// ------------------
// *errors.fundamental: oops
//
// [StackTrace]
// github.com/m-mizutani/zlog_test.crash
// 	/Users/mizutani/.ghq/github.com/m-mizutani/zlog_test/main.go:xx
// github.com/m-mizutani/zlog_test.main
// 	/Users/mizutani/.ghq/github.com/m-mizutani/zlog_test/main.go:xx
// runtime.main
//	/usr/local/Cellar/go/1.17/libexec/src/runtime/proc.go:255
// runtime.goexit
//	/usr/local/Cellar/go/1.17/libexec/src/runtime/asm_amd64.s:1581
// ------------------

Output error related values

func crash(args string) error {
	return goerr.New("oops").With("args", args)
}

func main() {
	logger := zlog.New()
	if err := crash("hello"); err != nil {
		logger.Err(err).Error("failed")
	}
}

// Output:
// [error] failed
//
// ------------------
// *goerr.Error: oops
//
// (snip)
//
// [Values]
// args => "hello"
// ------------------

License

Owner
Masayoshi Mizutani
Security + Software Engineer
Masayoshi Mizutani
Similar Resources

Nginx-Log-Analyzer is a lightweight (simplistic) log analyzer for Nginx.

Nginx-Log-Analyzer is a lightweight (simplistic) log analyzer for Nginx.

Nginx-Log-Analyzer is a lightweight (simplistic) log analyzer, used to analyze Nginx access logs for myself.

Nov 29, 2022

Distributed-Log-Service - Distributed Log Service With Golang

Distributed-Log-Service - Distributed Log Service With Golang

Distributed Log Service This project is essentially a result of my attempt to un

Jun 1, 2022

Log-analyzer - Log analyzer with golang

Log Analyzer what do we have here? Objective Installation and Running Applicatio

Jan 27, 2022

Changelog management tool, avoid merge conflicts and generate markdown changelogs.

chalog This is chalog, a changelog management tool. With chalog you can manage your project's changelog in a simple markdown format, split across mult

Jul 7, 2022

A logger, for Go

Go-Log A logger, for Go! It's sort of log and code.google.com/p/log4go compatible, so in most cases can be used without any code changes. Breaking cha

Oct 7, 2022

Simple logger for Go programs. Allows custom formats for messages.

Simple logger for Go programs. Allows custom formats for messages.

go-logger A simple go logger for easy logging in your programs. Allows setting custom format for messages. Preview Install go get github.com/apsdehal/

Dec 17, 2022

Loggly Hooks for GO Logrus logger

Loggly Hooks for Logrus Usage package main import ( "github.com/sirupsen/logrus" "github.com/sebest/logrusly" ) var logglyToken string = "YOUR_LOG

Sep 26, 2022

A 12-factor app logger built for performance and happy development

A 12-factor app logger built for performance and happy development

logxi log XI is a structured 12-factor app logger built for speed and happy development. Simpler. Sane no-configuration defaults out of the box. Faste

Nov 27, 2022

Dead simple, super fast, zero allocation and modular logger for Golang

Onelog Onelog is a dead simple but very efficient JSON logger. It is one of the fastest JSON logger out there. Also, it is one of the logger with the

Sep 26, 2022
Convenient Logger interface and std logger wrapper

Convenient logger interface and wrapper around std logger Interface type Logger interface { Error(err error) Debugf(format string, args ...interface

Nov 28, 2021
Logger - Simple logger without written with std pkg

Go-Logger Simple usage is: package main

Jan 2, 2022
Logger - A thin wrapper of uber-go/zap logger for personal project

a thin wraper of uber-go/zap logger for personal project 0. thanks uber-go/zap B

Sep 17, 2022
Parametrized JSON logging library in Golang which lets you obfuscate sensitive data and marshal any kind of content.
Parametrized JSON logging library in Golang which lets you obfuscate sensitive data and marshal any kind of content.

Noodlog Summary Noodlog is a Golang JSON parametrized and highly configurable logging library. It allows you to: print go structs as JSON messages; pr

Oct 27, 2022
Binalyze logger is an easily customizable wrapper for logrus with log rotation

logger logger is an easily customizable wrapper for logrus with log rotation Usage There is only one function to initialize logger. logger.Init() When

Oct 2, 2022
Multi-level logger based on go std log

mlog the mlog is multi-level logger based on go std log. It is: Simple Easy to use NOTHING ELSE package main import ( log "github.com/ccpaging/lo

May 18, 2022
Logger - Go language is interface-oriented to implement an asynchronous log writing program

logger日志库 1、安装 go get github.com/staryjie/logger@latest 2、使用 示例: package main import ( "github.com/staryjie/logger" "time" ) func initLogger(name,

Jan 4, 2022
A customized GORM logger that implements the appropriate interface and uses Logrus to output logs

CryptoMath GORM Logger A customized GORM logger that implements the appropriate interface and uses Logrus to output logs. Install go get github.com/ma

Nov 6, 2021
This package enables json output, level logging and so on to standard go logger.

logplug This package enables json output, level logging and so on to standard logger. Usage log.SetOutput(logplug.NewJSONPlug(os.Stderr, logplug.LogF

Dec 27, 2021
An golang log lib, supports tracking and level, wrap by standard log lib

Logex An golang log lib, supports tracing and level, wrap by standard log lib How To Get shell go get gopkg.in/logex.v1 source code import "gopkg.in/

Nov 27, 2022