func (l LoggerWithCtx) Warn(msg string, fields ...zapcore.Field) {
	l.l.logFields(l.ctx, zap.WarnLevel, msg, fields)
	l.l.Warn(msg, fields...)
}
Warn logs a message at WarnLevel. The message includes any fields passed
at the log site, as well as any fields accumulated on the logger.
package otelzap
import (
	"context"
	"encoding/json"
	"fmt"
	"math"
	"reflect"
	"runtime"
	"strconv"
	"time"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/codes"
	semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
	"go.opentelemetry.io/otel/trace"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)
const numAttr = 5
var (
	logSeverityKey = attribute.Key("log.severity")
	logMessageKey  = attribute.Key("log.message")
	logTemplateKey = attribute.Key("log.template")
)
type Logger struct {
	*zap.Logger
	withTraceID bool
	minLevel         zapcore.Level
	errorStatusLevel zapcore.Level
	caller     bool
	stackTrace bool
}
func Wrap(logger *zap.Logger, opts ...Option) *Logger {
	return New(logger, opts...)
}
func New(logger *zap.Logger, opts ...Option) *Logger {
	l := &Logger{
		Logger: logger,
		minLevel:         zap.WarnLevel,
		errorStatusLevel: zap.ErrorLevel,
		caller:           true,
	}
	for _, opt := range opts {
		opt(l)
	}
	return l
}
func (l *Logger) WithOptions(opts ...zap.Option) *Logger {
	clone := *l
	clone.Logger = l.Logger.WithOptions(opts...)
	return &clone
}
func (l *Logger) Sugar() *SugaredLogger {
	return &SugaredLogger{
		SugaredLogger: l.Logger.Sugar(),
		l:             l,
	}
}
func (l *Logger) Clone(opts ...Option) *Logger {
	clone := *l
	for _, opt := range opts {
		opt(&clone)
	}
	return &clone
}
func (l *Logger) Ctx(ctx context.Context) LoggerWithCtx {
	return LoggerWithCtx{
		ctx: ctx,
		l:   l,
	}
}
func (l *Logger) DebugContext(ctx context.Context, msg string, fields ...zapcore.Field) {
	fields = l.logFields(ctx, zap.DebugLevel, msg, fields)
	l.Debug(msg, fields...)
}
func (l *Logger) InfoContext(ctx context.Context, msg string, fields ...zapcore.Field) {
	fields = l.logFields(ctx, zap.InfoLevel, msg, fields)
	l.Info(msg, fields...)
}
func (l *Logger) WarnContext(ctx context.Context, msg string, fields ...zapcore.Field) {
	fields = l.logFields(ctx, zap.WarnLevel, msg, fields)
	l.Warn(msg, fields...)
}
func (l *Logger) ErrorContext(ctx context.Context, msg string, fields ...zapcore.Field) {
	fields = l.logFields(ctx, zap.ErrorLevel, msg, fields)
	l.Error(msg, fields...)
}
func (l *Logger) DPanicContext(ctx context.Context, msg string, fields ...zapcore.Field) {
	fields = l.logFields(ctx, zap.DPanicLevel, msg, fields)
	l.DPanic(msg, fields...)
}
func (l *Logger) PanicContext(ctx context.Context, msg string, fields ...zapcore.Field) {
	fields = l.logFields(ctx, zap.PanicLevel, msg, fields)
	l.Panic(msg, fields...)
}
func (l *Logger) FatalContext(ctx context.Context, msg string, fields ...zapcore.Field) {
	fields = l.logFields(ctx, zap.FatalLevel, msg, fields)
	l.Fatal(msg, fields...)
}
func (l *Logger) logFields(
	ctx context.Context, lvl zapcore.Level, msg string, fields []zapcore.Field,
) []zapcore.Field {
	if lvl < l.minLevel {
		return fields
	}
	span := trace.SpanFromContext(ctx)
	if !span.IsRecording() {
		return fields
	}
	attrs := make([]attribute.KeyValue, 0, numAttr+len(fields))
	for _, f := range fields {
		if f.Type == zapcore.NamespaceType {
			continue
		}
		attrs = appendField(attrs, f)
	}
	l.log(span, lvl, msg, attrs)
	if l.withTraceID {
		traceID := span.SpanContext().TraceID().String()
		fields = append(fields, zap.String("trace_id", traceID))
	}
	return fields
}
func (l *Logger) log(
	span trace.Span, lvl zapcore.Level, msg string, attrs []attribute.KeyValue,
) {
	attrs = append(attrs, logSeverityKey.String(levelString(lvl)))
	attrs = append(attrs, logMessageKey.String(msg))
	if l.caller {
		if fn, file, line, ok := runtimeCaller(4); ok {
			if fn != "" {
				attrs = append(attrs, semconv.CodeFunctionKey.String(fn))
			}
			if file != "" {
				attrs = append(attrs, semconv.CodeFilepathKey.String(file))
				attrs = append(attrs, semconv.CodeLineNumberKey.Int(line))
			}
		}
	}
	if l.stackTrace {
		stackTrace := make([]byte, 2048)
		n := runtime.Stack(stackTrace, false)
		attrs = append(attrs, semconv.ExceptionStacktraceKey.String(string(stackTrace[0:n])))
	}
	span.AddEvent("log", trace.WithAttributes(attrs...))
	if lvl >= l.errorStatusLevel {
		span.SetStatus(codes.Error, msg)
	}
}
func runtimeCaller(skip int) (fn, file string, line int, ok bool) {
	rpc := make([]uintptr, 1)
	n := runtime.Callers(skip+1, rpc[:])
	if n < 1 {
		return
	}
	frame, _ := runtime.CallersFrames(rpc).Next()
	return frame.Function, frame.File, frame.Line, frame.PC != 0
}
type LoggerWithCtx struct {
	ctx context.Context
	l   *Logger
}
func (l LoggerWithCtx) Context() context.Context {
	return l.ctx
}
func (l LoggerWithCtx) Logger() *Logger {
	return l.l
}
func (l LoggerWithCtx) ZapLogger() *zap.Logger {
	return l.l.Logger
}
func (l LoggerWithCtx) WithOptions(opts ...zap.Option) LoggerWithCtx {
	return LoggerWithCtx{
		ctx: l.ctx,
		l:   l.l.WithOptions(opts...),
	}
}
func (l LoggerWithCtx) Clone(opts ...Option) LoggerWithCtx {
	return LoggerWithCtx{
		ctx: l.ctx,
		l:   l.l.Clone(opts...),
	}
}
func (l LoggerWithCtx) Debug(msg string, fields ...zapcore.Field) {
	l.l.logFields(l.ctx, zap.DebugLevel, msg, fields)
	l.l.Debug(msg, fields...)
}
func (l LoggerWithCtx) Info(msg string, fields ...zapcore.Field) {
	l.l.logFields(l.ctx, zap.InfoLevel, msg, fields)
	l.l.Info(msg, fields...)
}