0

How to print the exact source line using zap. I create a package where it stores variable(zap logger). This global variable will be called inside functions in that package. The problem is it's not printing the actual caller, instead it calls where the logger called inside function inside the logger package. Here is my code:

package log

import (
    zap "go.uber.org/zap"
)

var logger        *zap.Logger

func Init() {
    logger,_ = zap.NewDevelopment()
}

func Info(msg interface{}) {
    if v,ok := msg.(string); ok {
        logger.Info(v) // when I called this function it always print the caller from this line, not the caller outside who actually called this function.
    } else {
        logger.Info(fmt.Sprintf("%v",msg))
    }
}

What's workaround to print value of source line for the actual caller?

blackgreen
  • 34,072
  • 23
  • 111
  • 129
Jake Muller
  • 925
  • 4
  • 18
  • 25
  • One of the main ideas of zap is to reduce allocations and avoid the empty interface. Your `Info` function violates the whole purpose of zap. – Volker Apr 16 '20 at 09:32
  • 1
    Can you elaborate more? @Volker – Jake Muller Apr 16 '20 at 09:54
  • So the only thing I need to do is to using string instead of interface?, Ok then. – Jake Muller Apr 16 '20 at 09:56
  • 2
    Along with the accepted answer, you can use the generalised configuration option: https://github.com/uber-go/zap/blob/master/options.go#L104 / `AddCallerSkip` – Martin Gallagher Apr 16 '20 at 10:19
  • @MartinGallagher wow, that's amazing, didn't know that. Thanks. – Jake Muller Apr 16 '20 at 10:48
  • 2
    If you read about the design of zap they tried to make it typesafe and minimize the allocations done if something gets logged. Your Info function taks whatever it is passed, wraps it in and interface{} value, inspects that value, converts it to a string if not yet one via fmt.Sprintf (!!) and uses that as the info message. The message should always be a string anyway, you should not log arbitrary data as the message, that's what fields are for. – Volker Apr 16 '20 at 11:20

2 Answers2

2

Use runtime caller.

import "runtime"

// call below line inside your Info function.
pc, src, line, ok := runtime.Caller(1)

src and line are what you need.

mfathirirhas
  • 2,169
  • 5
  • 23
  • 35
0
var (
    //APP_ENV for config
    APP_ENV = "APP_ENV"
)

func init() {
    BuildLogger(os.Getenv(APP_ENV))
}

// BuildLogger builds log config
func BuildLogger(env string) {
    var outputPaths []string
    var level zapcore.Level

    if env == "development" || env == "" {
        outputPaths = []string{"stdout"}
        level = zapcore.DebugLevel
    } else if env == "production" {
        outputPaths = setOutputPaths()
        level = zapcore.InfoLevel
    }

    config = zap.Config{
        Level:       zap.NewAtomicLevelAt(level),
        Development: false,
        Sampling: &zap.SamplingConfig{
            Initial:    100,
            Thereafter: 100,
        },
        Encoding:         "json",
        EncoderConfig:    zap.NewProductionEncoderConfig(),
        OutputPaths:      outputPaths,
        ErrorOutputPaths: []string{"stderr"},
    }

    logger, err := config.Build(zap.AddCallerSkip(1))
    if err != nil {
        panic(err)
    }

    log = logger.Sugar()
}

This is my logger configuration. Added logger, err := config.Build(zap.AddCallerSkip(1)) this line and worked, caller changed.

Sefa Şahin
  • 46
  • 1
  • 5