diff --git a/README.md b/README.md index b547cc68e..2793ee609 100755 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ TBD: throughput on production servers. **Input**: [dmesg](plugin/input/dmesg/README.md), [fake](plugin/input/fake/README.md), [file](plugin/input/file/README.md), [http](plugin/input/http/README.md), [journalctl](plugin/input/journalctl/README.md), [k8s](plugin/input/k8s/README.md), [kafka](plugin/input/kafka/README.md) -**Action**: [add_file_name](plugin/action/add_file_name/README.md), [add_host](plugin/action/add_host/README.md), [convert_date](plugin/action/convert_date/README.md), [convert_log_level](plugin/action/convert_log_level/README.md), [convert_utf8_bytes](plugin/action/convert_utf8_bytes/README.md), [debug](plugin/action/debug/README.md), [discard](plugin/action/discard/README.md), [flatten](plugin/action/flatten/README.md), [join](plugin/action/join/README.md), [join_template](plugin/action/join_template/README.md), [json_decode](plugin/action/json_decode/README.md), [json_encode](plugin/action/json_encode/README.md), [json_extract](plugin/action/json_extract/README.md), [keep_fields](plugin/action/keep_fields/README.md), [mask](plugin/action/mask/README.md), [modify](plugin/action/modify/README.md), [move](plugin/action/move/README.md), [parse_es](plugin/action/parse_es/README.md), [parse_http](plugin/action/parse_http/README.md), [parse_re2](plugin/action/parse_re2/README.md), [remove_fields](plugin/action/remove_fields/README.md), [rename](plugin/action/rename/README.md), [set_time](plugin/action/set_time/README.md), [split](plugin/action/split/README.md), [throttle](plugin/action/throttle/README.md) +**Action**: [add_file_name](plugin/action/add_file_name/README.md), [add_host](plugin/action/add_host/README.md), [convert_date](plugin/action/convert_date/README.md), [convert_log_level](plugin/action/convert_log_level/README.md), [convert_utf8_bytes](plugin/action/convert_utf8_bytes/README.md), [debug](plugin/action/debug/README.md), [discard](plugin/action/discard/README.md), [flatten](plugin/action/flatten/README.md), [join](plugin/action/join/README.md), [join_template](plugin/action/join_template/README.md), [json_decode](plugin/action/json_decode/README.md), [json_encode](plugin/action/json_encode/README.md), [json_extract](plugin/action/json_extract/README.md), [keep_fields](plugin/action/keep_fields/README.md), [mask](plugin/action/mask/README.md), [modify](plugin/action/modify/README.md), [move](plugin/action/move/README.md), [parse_es](plugin/action/parse_es/README.md), [parse_re2](plugin/action/parse_re2/README.md), [remove_fields](plugin/action/remove_fields/README.md), [rename](plugin/action/rename/README.md), [set_time](plugin/action/set_time/README.md), [split](plugin/action/split/README.md), [throttle](plugin/action/throttle/README.md) **Output**: [clickhouse](plugin/output/clickhouse/README.md), [devnull](plugin/output/devnull/README.md), [elasticsearch](plugin/output/elasticsearch/README.md), [file](plugin/output/file/README.md), [gelf](plugin/output/gelf/README.md), [kafka](plugin/output/kafka/README.md), [postgres](plugin/output/postgres/README.md), [s3](plugin/output/s3/README.md), [splunk](plugin/output/splunk/README.md), [stdout](plugin/output/stdout/README.md) diff --git a/_sidebar.md b/_sidebar.md index 972365fa2..8fff955a2 100644 --- a/_sidebar.md +++ b/_sidebar.md @@ -40,7 +40,6 @@ - [modify](plugin/action/modify/README.md) - [move](plugin/action/move/README.md) - [parse_es](plugin/action/parse_es/README.md) - - [parse_http](plugin/action/parse_http/README.md) - [parse_re2](plugin/action/parse_re2/README.md) - [remove_fields](plugin/action/remove_fields/README.md) - [rename](plugin/action/rename/README.md) diff --git a/charts/filed/README.md b/charts/filed/README.md index 3b231d954..78e3bf3ff 100644 --- a/charts/filed/README.md +++ b/charts/filed/README.md @@ -35,7 +35,7 @@ curl -X GET "http://localhost:9200/k8s-logs/_search/?size=10" -H 'Content-Type: ```shell helm upgrade --install web-logs . -f values.minikube.web-logs.yaml -curl --resolve "web-logs.local:80:$( minikube ip )" -H 'Content-Type: application/json' http://web-logs.local -d \ +curl --resolve "web-logs.local:80:$( minikube ip )" -H 'Content-Type: application/json' 'http://web-logs.local/?env=cli' -d \ '{"message": "Test event", "level": "info"}' kubectl port-forward svc/web-logs-elasticsearch 9201:9200 diff --git a/charts/filed/values.minikube.web-logs.yaml b/charts/filed/values.minikube.web-logs.yaml index 2288ea961..bc7a87786 100644 --- a/charts/filed/values.minikube.web-logs.yaml +++ b/charts/filed/values.minikube.web-logs.yaml @@ -30,6 +30,23 @@ elasticsearch: input: type: http address: ":80" + meta: + remote_addr: "{{ .remote_addr }}" + user_agent: '{{ index (index .request.Header "User-Agent") 0}}' + env: '{{ index (index .params "env") 0}}' + cors: + allowed_headers: + - DNT + - X-CustomHeader + - Keep-Alive + - User-Agent + - X-Requested-With + - If-Modified-Since + - Cache-Control + - Content-Type + - Authorization + allowed_origins: + - http://localhost:8090 actions: - type: set_time - type: convert_log_level @@ -37,6 +54,12 @@ actions: style: number default_level: info remove_on_fail: true + - type: mask + metric_name: errors_total + metric_skip_status: true + metric_labels: + - env + - level resourceType: Deployment replicas: 1 diff --git a/cmd/file.d/file.d.go b/cmd/file.d/file.d.go index 3ebc7ee5d..28e6acebc 100644 --- a/cmd/file.d/file.d.go +++ b/cmd/file.d/file.d.go @@ -33,7 +33,6 @@ import ( _ "github.com/ozontech/file.d/plugin/action/modify" _ "github.com/ozontech/file.d/plugin/action/move" _ "github.com/ozontech/file.d/plugin/action/parse_es" - _ "github.com/ozontech/file.d/plugin/action/parse_http" _ "github.com/ozontech/file.d/plugin/action/parse_re2" _ "github.com/ozontech/file.d/plugin/action/remove_fields" _ "github.com/ozontech/file.d/plugin/action/rename" diff --git a/docs/installation.md b/docs/installation.md index 82eb65b35..2658b74cd 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -17,6 +17,10 @@ Mount config from /my-config.yaml and start `file.d` with this config:
Do not forget to set [the actual tag](https://github.com/ozontech/file.d/pkgs/container/file.d) of the image. +## Helm-chart + +[Helm-chart](/charts/filed/README.md) and examples for Minikube + ## Precompiled binaries Precompiled binaries for released versions are available in the diff --git a/e2e/http_file/config.yml b/e2e/http_file/config.yml index beb36cd49..c4fcd64df 100644 --- a/e2e/http_file/config.yml +++ b/e2e/http_file/config.yml @@ -10,10 +10,6 @@ pipelines: match_fields: should_drop: ok match_mode: or - - type: add_file_name - field: source_name - - type: parse_http - field: source_name - type: join field: log start: '/^(panic:)|(http: panic serving)/' diff --git a/e2e/start_work_test.go b/e2e/start_work_test.go index 8dd2fd74f..c3480b22b 100644 --- a/e2e/start_work_test.go +++ b/e2e/start_work_test.go @@ -33,7 +33,6 @@ import ( _ "github.com/ozontech/file.d/plugin/action/mask" _ "github.com/ozontech/file.d/plugin/action/modify" _ "github.com/ozontech/file.d/plugin/action/parse_es" - _ "github.com/ozontech/file.d/plugin/action/parse_http" _ "github.com/ozontech/file.d/plugin/action/parse_re2" _ "github.com/ozontech/file.d/plugin/action/remove_fields" _ "github.com/ozontech/file.d/plugin/action/rename" diff --git a/plugin/README.md b/plugin/README.md index ebc91a84d..750306a2d 100755 --- a/plugin/README.md +++ b/plugin/README.md @@ -611,87 +611,6 @@ It parses HTTP input using Elasticsearch `/_bulk` API format. It converts source > Check out the details in [Elastic Bulk API](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html). [More details...](plugin/action/parse_es/README.md) -## parse_http -It adds a field containing the source name to the event and extracts remote_ip and auth login. -It is only applicable for input plugin http. -You need add action add_file_name before it. - -**Example:** -Service for receiving events from a static page: -```yaml -pipelines: - example_http_pipeline: - input: - # define input type. - type: http - # define http port. - address: ":9400" - auth: - strategy: basic - secrets: - frontend: 398fc645-e660-45f4-96bb-53b7a2b120e4 - cors: - allowed_headers: - - DNT - - X-CustomHeader - - Keep-Alive - - User-Agent - - X-Requested-With - - If-Modified-Since - - Cache-Control - - Content-Type - - Authorization - allowed_origins: - - http://localhost:8090 - actions: - - type: add_file_name - field: source_name - # parse http info - - type: parse_http - field: source_name - allowed_params: - - env - - type: convert_log_level - field: level - style: number - default_level: info - remove_on_fail: true - - type: mask - metric_name: errors_total - metric_skip_status: true - metric_labels: - - login - - level - - type: remove_fields - fields: - - source_name - output: - type: stdout - # Or we can write to file: - # type: file - # target_file: "./output.txt" -``` - -Setup: -```bash -# run server. -# config.yaml should contains yaml config above. -go run ./cmd/file.d --config=config.yaml - -# now do requests. -curl "127.0.0.1:9400/?env=cli" -H 'Content-Type: application/json' -H 'Authorization: Basic ZnJvbnRlbmQ6Mzk4ZmM2NDUtZTY2MC00NWY0LTk2YmItNTNiN2EyYjEyMGU0' -d \ -'{"message": "Test event", "level": "info"}' - -# run nginx with static page -docker run -p 8090:80 -v `pwd`/plugin/action/parse_http:/usr/share/nginx/html -it --rm --name my-static-html-server nginx:alpine - -# open http://localhost:8090 and click "Send Log Request" button - -# check metric -curl localhost:9000/metrics | grep "file_d_pipeline_example_http_pipeline_errors_total_events_count_total" -``` - -[More details...](plugin/action/parse_http/README.md) ## parse_re2 It parses string from the event field using re2 expression with named subgroups and merges the result with the event root. diff --git a/plugin/action/README.md b/plugin/action/README.md index 1407282a6..d8aa675fb 100755 --- a/plugin/action/README.md +++ b/plugin/action/README.md @@ -442,87 +442,6 @@ It parses HTTP input using Elasticsearch `/_bulk` API format. It converts source > Check out the details in [Elastic Bulk API](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html). [More details...](plugin/action/parse_es/README.md) -## parse_http -It adds a field containing the source name to the event and extracts remote_ip and auth login. -It is only applicable for input plugin http. -You need add action add_file_name before it. - -**Example:** -Service for receiving events from a static page: -```yaml -pipelines: - example_http_pipeline: - input: - # define input type. - type: http - # define http port. - address: ":9400" - auth: - strategy: basic - secrets: - frontend: 398fc645-e660-45f4-96bb-53b7a2b120e4 - cors: - allowed_headers: - - DNT - - X-CustomHeader - - Keep-Alive - - User-Agent - - X-Requested-With - - If-Modified-Since - - Cache-Control - - Content-Type - - Authorization - allowed_origins: - - http://localhost:8090 - actions: - - type: add_file_name - field: source_name - # parse http info - - type: parse_http - field: source_name - allowed_params: - - env - - type: convert_log_level - field: level - style: number - default_level: info - remove_on_fail: true - - type: mask - metric_name: errors_total - metric_skip_status: true - metric_labels: - - login - - level - - type: remove_fields - fields: - - source_name - output: - type: stdout - # Or we can write to file: - # type: file - # target_file: "./output.txt" -``` - -Setup: -```bash -# run server. -# config.yaml should contains yaml config above. -go run ./cmd/file.d --config=config.yaml - -# now do requests. -curl "127.0.0.1:9400/?env=cli" -H 'Content-Type: application/json' -H 'Authorization: Basic ZnJvbnRlbmQ6Mzk4ZmM2NDUtZTY2MC00NWY0LTk2YmItNTNiN2EyYjEyMGU0' -d \ -'{"message": "Test event", "level": "info"}' - -# run nginx with static page -docker run -p 8090:80 -v `pwd`/plugin/action/parse_http:/usr/share/nginx/html -it --rm --name my-static-html-server nginx:alpine - -# open http://localhost:8090 and click "Send Log Request" button - -# check metric -curl localhost:9000/metrics | grep "file_d_pipeline_example_http_pipeline_errors_total_events_count_total" -``` - -[More details...](plugin/action/parse_http/README.md) ## parse_re2 It parses string from the event field using re2 expression with named subgroups and merges the result with the event root. diff --git a/plugin/action/parse_http/README.idoc.md b/plugin/action/parse_http/README.idoc.md deleted file mode 100644 index 147b684ab..000000000 --- a/plugin/action/parse_http/README.idoc.md +++ /dev/null @@ -1,5 +0,0 @@ -# Parse HTTP plugin -@introduction - -### Config params -@config-params|description diff --git a/plugin/action/parse_http/README.md b/plugin/action/parse_http/README.md deleted file mode 100755 index 450d570e4..000000000 --- a/plugin/action/parse_http/README.md +++ /dev/null @@ -1,96 +0,0 @@ -# Parse HTTP plugin -It adds a field containing the source name to the event and extracts remote_ip and auth login. -It is only applicable for input plugin http. -You need add action add_file_name before it. - -**Example:** -Service for receiving events from a static page: -```yaml -pipelines: - example_http_pipeline: - input: - # define input type. - type: http - # define http port. - address: ":9400" - auth: - strategy: basic - secrets: - frontend: 398fc645-e660-45f4-96bb-53b7a2b120e4 - cors: - allowed_headers: - - DNT - - X-CustomHeader - - Keep-Alive - - User-Agent - - X-Requested-With - - If-Modified-Since - - Cache-Control - - Content-Type - - Authorization - allowed_origins: - - http://localhost:8090 - actions: - - type: add_file_name - field: source_name - # parse http info - - type: parse_http - field: source_name - allowed_params: - - env - - type: convert_log_level - field: level - style: number - default_level: info - remove_on_fail: true - - type: mask - metric_name: errors_total - metric_skip_status: true - metric_labels: - - login - - level - - type: remove_fields - fields: - - source_name - output: - type: stdout - # Or we can write to file: - # type: file - # target_file: "./output.txt" -``` - -Setup: -```bash -# run server. -# config.yaml should contains yaml config above. -go run ./cmd/file.d --config=config.yaml - -# now do requests. -curl "127.0.0.1:9400/?env=cli" -H 'Content-Type: application/json' -H 'Authorization: Basic ZnJvbnRlbmQ6Mzk4ZmM2NDUtZTY2MC00NWY0LTk2YmItNTNiN2EyYjEyMGU0' -d \ -'{"message": "Test event", "level": "info"}' - -# run nginx with static page -docker run -p 8090:80 -v `pwd`/plugin/action/parse_http:/usr/share/nginx/html -it --rm --name my-static-html-server nginx:alpine - -# open http://localhost:8090 and click "Send Log Request" button - -# check metric -curl localhost:9000/metrics | grep "file_d_pipeline_example_http_pipeline_errors_total_events_count_total" -``` - -### Config params -**`field`** *`cfg.FieldSelector`* *`default=source_name`* - -The event field to which put the source name. Must be a string. - - -
- -**`allowed_params`** *`[]string`* - -List of the allowed GET params. - -
- - -
*Generated using [__insane-doc__](https://github.com/vitkovskii/insane-doc)* \ No newline at end of file diff --git a/plugin/action/parse_http/index.html b/plugin/action/parse_http/index.html deleted file mode 100644 index b19afa307..000000000 --- a/plugin/action/parse_http/index.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - Send Log Request - - - - - - - diff --git a/plugin/action/parse_http/parse_http.go b/plugin/action/parse_http/parse_http.go deleted file mode 100644 index d8a59db57..000000000 --- a/plugin/action/parse_http/parse_http.go +++ /dev/null @@ -1,190 +0,0 @@ -package parse_http - -import ( - "encoding/base64" - "net/url" - "strings" - - "github.com/ozontech/file.d/cfg" - "github.com/ozontech/file.d/fd" - "github.com/ozontech/file.d/metric" - "github.com/ozontech/file.d/pipeline" - "github.com/prometheus/client_golang/prometheus" - "go.uber.org/zap" -) - -/*{ introduction -It adds a field containing the source name to the event and extracts remote_ip and auth login. -It is only applicable for input plugin http. -You need add action add_file_name before it. - -**Example:** -Service for receiving events from a static page: -```yaml -pipelines: - example_http_pipeline: - input: - # define input type. - type: http - # define http port. - address: ":9400" - auth: - strategy: basic - secrets: - frontend: 398fc645-e660-45f4-96bb-53b7a2b120e4 - cors: - allowed_headers: - - DNT - - X-CustomHeader - - Keep-Alive - - User-Agent - - X-Requested-With - - If-Modified-Since - - Cache-Control - - Content-Type - - Authorization - allowed_origins: - - http://localhost:8090 - actions: - - type: add_file_name - field: source_name - # parse http info - - type: parse_http - field: source_name - allowed_params: - - env - - type: convert_log_level - field: level - style: number - default_level: info - remove_on_fail: true - - type: mask - metric_name: errors_total - metric_skip_status: true - metric_labels: - - login - - level - - type: remove_fields - fields: - - source_name - output: - type: stdout - # Or we can write to file: - # type: file - # target_file: "./output.txt" -``` - -Setup: -```bash -# run server. -# config.yaml should contains yaml config above. -go run ./cmd/file.d --config=config.yaml - -# now do requests. -curl "127.0.0.1:9400/?env=cli" -H 'Content-Type: application/json' -H 'Authorization: Basic ZnJvbnRlbmQ6Mzk4ZmM2NDUtZTY2MC00NWY0LTk2YmItNTNiN2EyYjEyMGU0' -d \ -'{"message": "Test event", "level": "info"}' - -# run nginx with static page -docker run -p 8090:80 -v `pwd`/plugin/action/parse_http:/usr/share/nginx/html -it --rm --name my-static-html-server nginx:alpine - -# open http://localhost:8090 and click "Send Log Request" button - -# check metric -curl localhost:9000/metrics | grep "file_d_pipeline_example_http_pipeline_errors_total_events_count_total" -``` -}*/ - -type Plugin struct { - logger *zap.SugaredLogger - config *Config - allowedParams map[string]struct{} - eventsProcessedMetric *prometheus.CounterVec -} - -// ! config-params -// ^ config-params -type Config struct { - // > @3@4@5@6 - // > - // > The event field to which put the source name. Must be a string. - // > - Field cfg.FieldSelector `json:"field" parse:"selector" required:"false" default:"source_name"` // * - Field_ []string - - // > @3@4@5@6 - // > - // > List of the allowed GET params. - AllowedParams []string `json:"allowed_params"` // * -} - -func init() { - fd.DefaultPluginRegistry.RegisterAction(&pipeline.PluginStaticInfo{ - Type: "parse_http", - Factory: factory, - }) -} - -func factory() (pipeline.AnyPlugin, pipeline.AnyConfig) { - return &Plugin{}, &Config{} -} - -func (p *Plugin) Start(config pipeline.AnyConfig, params *pipeline.ActionPluginParams) { - p.config = config.(*Config) - p.logger = params.Logger - - if len(p.config.AllowedParams) > 0 { - p.allowedParams = make(map[string]struct{}, len(p.config.AllowedParams)) - for _, field := range p.config.AllowedParams { - p.allowedParams[field] = struct{}{} - } - } - - p.registerMetrics(params.MetricCtl) -} - -func (p *Plugin) registerMetrics(ctl *metric.Ctl) { - p.eventsProcessedMetric = ctl.RegisterCounterVec("events_processed_total", "How many events processed", "login") -} - -func (p *Plugin) Stop() { -} - -func (p *Plugin) Do(event *pipeline.Event) pipeline.ActionResult { - node := event.Root.Dig(p.config.Field_...) - sourceName := node.AsString() - - sourceInfo := strings.Split(sourceName, "_") - - if sourceInfo[0] != "http" { - p.logger.Error( - "Wrong format. Do you have http input plugin or add_file_name action? Maybe wrong field? You got: ", - zap.String("sourceName", sourceName), - ) - - return pipeline.ActionPass - } - - infoStr, _ := base64.StdEncoding.DecodeString(sourceInfo[1]) - info := strings.Split(string(infoStr), "_") - login := info[0] - rawQuery, _ := base64.StdEncoding.DecodeString(info[2]) - - params, _ := url.ParseQuery(string(rawQuery)) - if v, exists := params["login"]; exists { - login = v[0] - } - - pipeline.CreateNestedField(event.Root, []string{"login"}).MutateToString(login) - pipeline.CreateNestedField(event.Root, []string{"remote_ip"}).MutateToString(info[1]) - - p.eventsProcessedMetric.With(prometheus.Labels{"login": login}).Inc() - - for k, v := range params { - _, paramIsAllowed := p.allowedParams[k] - if paramIsAllowed { - pipeline.CreateNestedField(event.Root, []string{k}).MutateToString(v[0]) - } - } - - return pipeline.ActionPass -} diff --git a/plugin/action/parse_http/parse_http_test.go b/plugin/action/parse_http/parse_http_test.go deleted file mode 100644 index 4c15cfc5b..000000000 --- a/plugin/action/parse_http/parse_http_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package parse_http - -import ( - "encoding/base64" - "net/url" - "sync" - "testing" - - "github.com/ozontech/file.d/pipeline" - "github.com/ozontech/file.d/test" - "github.com/stretchr/testify/assert" -) - -func TestDo(t *testing.T) { - sourceName := "http_" - - login := "login" - ip := "127.0.0.1" - - vals := url.Values{} - whiteListParam := "whiteListParam" - whiteListValue := "whiteListValue" - vals.Add(whiteListParam, whiteListValue) - blackListParam := "blackListParam" - blackListValue := "blackListValue" - vals.Add(blackListParam, blackListValue) - - config := test.NewConfig(&Config{ - Field: "source_name", - AllowedParams: []string{ - whiteListParam, - }, - }, nil) - p, input, output := test.NewPipelineMock(test.NewActionPluginStaticInfo(factory, config, pipeline.MatchModeAnd, nil, false)) - wg := &sync.WaitGroup{} - wg.Add(1) - infoStr := login + "_" + ip + "_" + base64.StdEncoding.EncodeToString([]byte(vals.Encode())) - sourceName += base64.StdEncoding.EncodeToString([]byte(infoStr)) - - output.SetOutFn(func(e *pipeline.Event) { - assert.Equal(t, login, e.Root.Dig("login").AsString(), "wrong field value") - assert.Equal(t, ip, e.Root.Dig("remote_ip").AsString(), "wrong field value") - assert.Equal(t, whiteListValue, e.Root.Dig(whiteListParam).AsString(), "wrong field value") - assert.Empty(t, e.Root.Dig(blackListParam).AsString(), "wrong field value") - - wg.Done() - }) - - input.In(0, sourceName, 0, []byte(`{"source_name":"`+sourceName+`"}`)) - - wg.Wait() - p.Stop() -} diff --git a/plugin/input/http/README.md b/plugin/input/http/README.md index ed368254a..240b9ac73 100755 --- a/plugin/input/http/README.md +++ b/plugin/input/http/README.md @@ -139,4 +139,6 @@ Key uses in the http_input_total metric. **`request`** *`http.Request`* +**`params`** *`url.Values`* +
*Generated using [__insane-doc__](https://github.com/vitkovskii/insane-doc)* \ No newline at end of file diff --git a/plugin/input/http/http.go b/plugin/input/http/http.go index 1beba889b..be2459045 100644 --- a/plugin/input/http/http.go +++ b/plugin/input/http/http.go @@ -595,11 +595,11 @@ func (p *Plugin) putGzipReader(reader *gzip.Reader) { func getUserIP(r *http.Request) net.IP { var userIP string switch { - case len(r.Header.Get("CF-Connecting-IP")) > 1: + case r.Header.Get("CF-Connecting-IP") != "": userIP = r.Header.Get("CF-Connecting-IP") - case len(r.Header.Get("X-Forwarded-For")) > 1: + case r.Header.Get("X-Forwarded-For") != "": userIP = r.Header.Get("X-Forwarded-For") - case len(r.Header.Get("X-Real-IP")) > 1: + case r.Header.Get("X-Real-IP") != "": userIP = r.Header.Get("X-Real-IP") default: userIP = r.RemoteAddr