001/* 002 * (C) Copyright 2018 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bdelbosc 018 */ 019package org.nuxeo.lib.stream.log; 020 021import static java.lang.Math.max; 022 023import java.io.IOException; 024import java.util.List; 025import java.util.Objects; 026import java.util.stream.Collectors; 027 028import com.fasterxml.jackson.databind.JsonNode; 029import com.fasterxml.jackson.databind.ObjectMapper; 030 031/** 032 * Extends LogLag with lower and upper timestamps to express lag as a latency. 033 * 034 * @since 10.1 035 */ 036public class Latency { 037 protected final LogLag lag; 038 039 protected final long lower; 040 041 protected final long upper; 042 043 protected final String key; 044 045 public Latency(long lower, long upper, LogLag lag, String key) { 046 Objects.requireNonNull(lag); 047 this.lower = lower; 048 this.upper = upper; 049 this.lag = lag; 050 this.key = key; 051 } 052 053 public static Latency noLatency(long upper, LogLag lag) { 054 Objects.requireNonNull(lag); 055 if (lag.lag() != 0) { 056 throw new IllegalArgumentException("Lag found: " + lag); 057 } 058 return new Latency(0, upper, lag, null); 059 } 060 061 public static Latency fromJson(String json) { 062 ObjectMapper mapper = new ObjectMapper(); 063 try { 064 JsonNode obj = mapper.readTree(json); 065 long lower = obj.get("low").asLong(); 066 long upper = obj.get("up").asLong(); 067 long lag = obj.get("lag").asLong(); 068 String key = obj.get("key") == null ? null : obj.get("key").asText(); 069 return new Latency(lower, upper, LogLag.of(lag), key); 070 } catch (IOException e) { 071 throw new IllegalArgumentException("Invalid json: " + json, e); 072 } 073 } 074 075 public static Latency of(List<Latency> latencies) { 076 LogLag lag = LogLag.of(latencies.stream().map(Latency::lag).collect(Collectors.toList())); 077 final long[] start = { Long.MAX_VALUE }; 078 final long[] end = { 0 }; 079 final String[] key = { "" }; 080 latencies.forEach(item -> { 081 if (item.lower > 0 && item.lower < start[0]) { 082 start[0] = item.lower; 083 key[0] = item.key; 084 } 085 end[0] = max(end[0], item.upper); 086 }); 087 return new Latency(start[0] == Long.MAX_VALUE ? 0 : start[0], end[0], lag, key[0]); 088 } 089 090 /** 091 * Returns the latency expressed in millisecond. 092 */ 093 public long latency() { 094 return lag.lag() > 0 ? upper - lower : 0; 095 } 096 097 /** 098 * Returns the lower timestamp. 099 */ 100 public long lower() { 101 return lower; 102 } 103 104 /** 105 * Returns the upper timestamp. 106 */ 107 public long upper() { 108 return upper; 109 } 110 111 public LogLag lag() { 112 return lag; 113 } 114 115 /** 116 * Returns the key associated with the lower timestamp. 117 */ 118 public String key() { 119 return key; 120 } 121 122 @Override 123 public String toString() { 124 return "Latency{" + "lat=" + latency() + ", lower=" + lower + ", upper=" + upper + ", key=" + key + ", lag=" 125 + lag + '}'; 126 } 127 128 public String asJson() { 129 return String.format("{\"lat\":\"%s\",\"low\":\"%s\",\"up\":\"%s\",\"lag\":\"%s\"%s}", latency(), lower, upper, 130 lag.lag, key == null ? "" : ",\"key\":\"" + key + "\""); 131 } 132}