github.com/ngicks/gokugen@v0.0.5/middleware/log/log.go (about)

     1  package log
     2  
     3  //go:generate mockgen -source log.go -destination __mock/log.go
     4  
     5  import (
     6  	"context"
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"time"
    11  
    12  	"github.com/ngicks/gokugen"
    13  )
    14  
    15  type Logger interface {
    16  	Info(v any, logValues ...string)
    17  	Error(e error, logValues ...string)
    18  }
    19  
    20  type LogMiddleware struct {
    21  	logger           Logger
    22  	timeFormat       string
    23  	shouldLogParam   bool
    24  	additionalValues []any
    25  }
    26  
    27  type Option = func(mw *LogMiddleware) *LogMiddleware
    28  
    29  func SetTimeFormat(timeFormat string) (Option, error) {
    30  	_, err := time.Parse(timeFormat, time.Now().Format(timeFormat))
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  
    35  	return func(mw *LogMiddleware) *LogMiddleware {
    36  		mw.timeFormat = timeFormat
    37  		return mw
    38  	}, nil
    39  }
    40  
    41  func LogParam() Option {
    42  	return func(mw *LogMiddleware) *LogMiddleware {
    43  		mw.shouldLogParam = true
    44  		return mw
    45  	}
    46  }
    47  
    48  func LogAdditionalValues(keys ...any) Option {
    49  	return func(mw *LogMiddleware) *LogMiddleware {
    50  		mw.additionalValues = keys
    51  		return mw
    52  	}
    53  }
    54  
    55  func New(logger Logger, options ...Option) *LogMiddleware {
    56  	mw := &LogMiddleware{
    57  		logger:     logger,
    58  		timeFormat: time.RFC3339Nano,
    59  	}
    60  	for _, opt := range options {
    61  		mw = opt(mw)
    62  	}
    63  	if mw.additionalValues == nil {
    64  		mw.additionalValues = make([]any, 0)
    65  	}
    66  	return mw
    67  }
    68  
    69  func (mw *LogMiddleware) Middleware(handler gokugen.ScheduleHandlerFn) gokugen.ScheduleHandlerFn {
    70  	return func(ctx gokugen.SchedulerContext) (gokugen.Task, error) {
    71  		return handler(
    72  			gokugen.WrapContext(
    73  				ctx,
    74  				gokugen.WithWorkFnWrapper(wrapper(mw.logger, mw.buildLogValueSet)),
    75  			),
    76  		)
    77  	}
    78  }
    79  
    80  type logValueSetBuilder = func(ctx gokugen.SchedulerContext) (logValues []string)
    81  
    82  func wrapper(logger Logger, logVal logValueSetBuilder) func(self gokugen.SchedulerContext, workFn gokugen.WorkFn) gokugen.WorkFn {
    83  	return func(self gokugen.SchedulerContext, workFn gokugen.WorkFn) gokugen.WorkFn {
    84  		return func(taskCtx context.Context, scheduled time.Time) (any, error) {
    85  			values := logVal(self)
    86  			logger.Info(nil, append(values, "timing", "before_work")...)
    87  			ret, err := workFn(taskCtx, scheduled)
    88  			values = append(values, "timing", "after_work")
    89  			if err != nil {
    90  				logger.Error(err, values...)
    91  			} else {
    92  				logger.Info(ret, values...)
    93  			}
    94  			return ret, err
    95  		}
    96  	}
    97  }
    98  
    99  func (mw *LogMiddleware) buildLogValueSet(ctx gokugen.SchedulerContext) (logValues []string) {
   100  	logValues = append(logValues, "scheduled_at", ctx.ScheduledTime().Format(time.RFC3339Nano))
   101  
   102  	taskId, _ := gokugen.GetTaskId(ctx)
   103  	if taskId != "" {
   104  		logValues = append(logValues, "task_id", taskId)
   105  	}
   106  	workId, _ := gokugen.GetWorkId(ctx)
   107  	if workId != "" {
   108  		logValues = append(logValues, "work_id", workId)
   109  	}
   110  
   111  	if mw.shouldLogParam {
   112  		param, err := gokugen.GetParam(ctx)
   113  		if err == nil {
   114  			marshaled, err := json.Marshal(param)
   115  			if err == nil {
   116  				logValues = append(logValues, "param", string(marshaled))
   117  			} else {
   118  				logValues = append(logValues, "param", "marshalling error")
   119  			}
   120  		} else if !errors.Is(err, gokugen.ErrValueNotFound) {
   121  			logValues = append(logValues, "param", "loading error")
   122  		}
   123  	}
   124  
   125  	for _, key := range mw.additionalValues {
   126  		val, err := ctx.Value(key)
   127  		keyLabel := fmt.Sprintf("%v", key)
   128  		if err != nil && !errors.Is(err, gokugen.ErrValueNotFound) {
   129  			logValues = append(logValues, keyLabel, "loading error")
   130  		} else {
   131  			marshaled, err := json.Marshal(val)
   132  			if err == nil {
   133  				logValues = append(logValues, keyLabel, string(marshaled))
   134  			} else {
   135  				logValues = append(logValues, keyLabel, "marshalling error")
   136  			}
   137  		}
   138  	}
   139  
   140  	return
   141  }