# Dealing with repeated upstream fields in a Nginx log

**URL:** <https://discuss.elastic.co/t/dealing-with-repeated-upstream-fields-in-a-nginx-log/91352>\
**Category:** Logstash\
**Created:** [June 30, 2017, 1:10am UTC](https://discuss.elastic.co/t/dealing-with-repeated-upstream-fields-in-a-nginx-log/91352 "2017-06-30T01:10:00Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Simon\_Oxwell](https://avatars.discourse-cdn.com/v4/letter/s/f17d59/32.png) [@Simon\_Oxwell](https://discuss.elastic.co/u/Simon_Oxwell)\
**Post date:** [June 30, 2017, 1:10am UTC](https://discuss.elastic.co/t/dealing-with-repeated-upstream-fields-in-a-nginx-log/91352/1 "2017-06-30T01:10:00Z")

</div>

Hi,

I'm trying to parse Nginx logs where Nginx is acting as a front-side proxy to multiple upstream servers in such a way that a failure for one upstream server to respond will cause Nginx to try another server. As such, Nginx logs multiple upstream servers and multiple upstream response times.

So an access log entry basically looks like this:

```
10.20.30.40 - - [30/Jun/2017:09:34:28 +1000] http test.com 172.0.0.80:8080, 172.0.0.40:8080 0.000, 15.053 "GET /uri HTTP/1.1" 200 3960 "http://test.com/uri" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:54.0) Gecko/20100101 Firefox/54.0" "-"

```

Which we've been parsing with a grok filter like this:

```
  grok {
    match => { "message" => "%{IPORHOST:clientip} - %{USER:ident} \[%{HTTPDATE:timestamp}\] (%{NUMBER}|%{NOTSPACE:scheme}) %{HOSTNAME:httphost} (%{HOSTNAME:upstreamip}:%{NUMBER}|-), (%{HOSTNAME:upstreamip}:%{NUMBER}|-) (%{NUMBER:upstream_response_time:float}|-), (%{NUMBER:upstream_response_time:float}|-) \"(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})\" %{NUMBER:response} (?:%{NUMBER:bytes:int}|-) %{QS:referrer} %{QS:agent} %{QS:x_forwarded_for}" }
    add_field => { "upstream_failed" => "true" }
 }

```

This produces multiple upstreamip fields, and multiple upstream\_response\_time fields.

Currently, in kibana, the multiple upstreamip fields are shown as a comma separated list (eg, '172.0.0.80, 172.0.0.40', or as array in JSON view), but the upstream\_response\_time field shows only the first value, or possibly no value, showing only '0'.

So, not quite understanding why upstream\_response\_time isn't showing as a comma separated list/array.

Alternatively, is there a better way of handling this sort of log entry? Ultimately, I need to be able to access the sum of the upstream\_response\_time values, potentially via some aggregator in elasticsearch (rather than a raw entry?), whilst also preserving the original list of times.

Any suggestions are welcomed.

Running Logstash 5.4.0, Elasticsearch 5.4.0, Kibana 5.4.0, and the nginx files are shipped to logstash with filebeat 5.4.3.

Thanks,

Simon

---

<div class="post-metadata">

**Author:** ![Simon\_Oxwell](https://avatars.discourse-cdn.com/v4/letter/s/f17d59/32.png) [@Simon\_Oxwell](https://discuss.elastic.co/u/Simon_Oxwell)\
**Post date:** [July 25, 2017, 1:53am UTC](https://discuss.elastic.co/t/dealing-with-repeated-upstream-fields-in-a-nginx-log/91352/2 "2017-07-25T01:53:50Z")

</div>

Eventually dealt with this by doing this:

```
  grok {
    match => { "message" => "%{IPORHOST:clientip} - %{HTTPDUSER:ident} \[%{HTTPDATE:timestamp}\] (%{NUMBER}|%{NOTSPACE:scheme}) (%{HOSTNAME:httphost}|_) (?<upstreamip>%{HOSTNAME}(:%{NUMBER})?(, %{HOSTNAME}(:%{NUMBER})?)*) (?<upstream_response_times>(%{NUMBER}|-)(, (%{NUMBER}|-))*) \"(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})\" %{NUMBER:response} (?:%{NUMBER:bytes:int}|-) %{QS:referrer} %{QS:agent} %{QS:x_forwarded_for}( %{NUMBER:request_time:float})?" }
  }
  # don't try anything else if we still can't parse
  if !("_grokparsefailure" in [tags]) {
    # remove ':<portnum>' from the string of upstream servers
    # (Two mutate filters to preserve order)
    mutate {
      gsub => ["upstreamip", ":\d*", ""]
    }
    # convert the comma separated string to an array
    mutate {
      split => ["upstreamip", ", "]
    }
    # sum the upstream response times and remove the raw field
    ruby {
      code => "event.set('upstream_response_time', event.get('upstream_response_times').gsub(/-/, '0').split(', ').map(&:to_f).inject(:+))"
      remove_field => ['upstream_response_times']
    }
  }

```

I don't know if that's the most efficient, but it works well enough.

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)\
**Post date:** [August 22, 2017, 1:54am UTC](https://discuss.elastic.co/t/dealing-with-repeated-upstream-fields-in-a-nginx-log/91352/3 "2017-08-22T01:54:08Z")

</div>

Closed on August 22, 2017, 1:54am UTC

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.
