package accesslog import ( "errors" "net/http" "time" "github.com/yusing/godoxy/internal/serialization" ) type ( ConfigBase struct { Path string `json:"path,omitempty"` Stdout bool `json:"stdout"` Retention *Retention `json:"retention" aliases:"keep"` RotateInterval time.Duration `json:"rotate_interval,omitempty" swaggertype:"primitive,integer"` } // @name AccessLoggerConfigBase ACLLoggerConfig struct { ConfigBase LogAllowed bool `json:"log_allowed"` } // @name ACLLoggerConfig RequestLoggerConfig struct { ConfigBase Format Format `json:"format" validate:"oneof=common combined json"` Filters Filters `json:"filters"` Fields Fields `json:"fields"` } // @name RequestLoggerConfig Config struct { ConfigBase acl *ACLLoggerConfig req *RequestLoggerConfig } AnyConfig interface { ToConfig() *Config Writers() ([]File, error) } Format string Filters struct { StatusCodes LogFilter[*StatusCodeRange] `json:"status_codes,omitzero"` Method LogFilter[HTTPMethod] `json:"method,omitzero"` Host LogFilter[Host] `json:"host,omitzero"` Headers LogFilter[*HTTPHeader] `json:"headers,omitzero"` // header exists or header == value CIDR LogFilter[*CIDR] `json:"cidr,omitzero"` } Fields struct { Headers FieldConfig `json:"headers,omitzero" aliases:"header"` Query FieldConfig `json:"query,omitzero" aliases:"queries"` Cookies FieldConfig `json:"cookies,omitzero" aliases:"cookie"` } ) var ( FormatCommon Format = "common" FormatCombined Format = "combined" FormatJSON Format = "json" ReqLoggerFormats = []Format{FormatCommon, FormatCombined, FormatJSON} ) func (cfg *ConfigBase) Validate() error { if cfg.Path == "" && !cfg.Stdout { return errors.New("path or stdout is required") } return nil } // Writers returns a list of writers for the config. func (cfg *ConfigBase) Writers() ([]File, error) { writers := make([]File, 0, 2) if cfg.Path != "" { f, err := OpenFile(cfg.Path) if err != nil { return nil, err } writers = append(writers, f) } if cfg.Stdout { writers = append(writers, stdout) } return writers, nil } func (cfg *ACLLoggerConfig) ToConfig() *Config { return &Config{ ConfigBase: cfg.ConfigBase, acl: cfg, } } func (cfg *RequestLoggerConfig) ToConfig() *Config { return &Config{ ConfigBase: cfg.ConfigBase, req: cfg, } } func (cfg *Config) ShouldLogRequest(req *http.Request, res *http.Response) bool { if cfg.req == nil { return true } return cfg.req.Filters.StatusCodes.CheckKeep(req, res) && cfg.req.Filters.Method.CheckKeep(req, res) && cfg.req.Filters.Headers.CheckKeep(req, res) && cfg.req.Filters.CIDR.CheckKeep(req, res) } func DefaultRequestLoggerConfig() *RequestLoggerConfig { return &RequestLoggerConfig{ ConfigBase: ConfigBase{ Retention: &Retention{Days: 30}, }, Format: FormatCombined, Fields: Fields{ Headers: FieldConfig{ Default: FieldModeDrop, }, Query: FieldConfig{ Default: FieldModeKeep, }, Cookies: FieldConfig{ Default: FieldModeDrop, }, }, } } func DefaultACLLoggerConfig() *ACLLoggerConfig { return &ACLLoggerConfig{ ConfigBase: ConfigBase{ Retention: &Retention{Days: 30}, }, } } func init() { serialization.RegisterDefaultValueFactory(DefaultRequestLoggerConfig) serialization.RegisterDefaultValueFactory(DefaultACLLoggerConfig) }