Skip to content

Commit

Permalink
✨ optimise log monitor with stracktrace #277
Browse files Browse the repository at this point in the history
  • Loading branch information
trydofor committed Jul 23, 2024
1 parent 7ae3677 commit bfcf663
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 41 deletions.
1 change: 1 addition & 0 deletions WingsBoot.t.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ Use `t.md` as local [Test Management](https://www.jetbrains.com/help/idea/test-m
* 13125 JsonHelperCompatibleTest: jackson basic type compatible
* 13126 FastJsonTest: fastjson helper json path
* 13127 TypeReferenceTest: TypeReference, TypeDescriptor, ResolvableType
* 13128 LogViewerTest: only match header line

## 14 Warlock

Expand Down
2 changes: 1 addition & 1 deletion observe/docs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package pro.fessional.wings.slardar.monitor.viewer;

import io.swagger.v3.oas.annotations.Operation;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled;
import pro.fessional.wings.slardar.spring.prop.SlardarMonitorProp;

import java.io.IOException;

/**
* @author trydofor
* @since 2021-07-20
*/
@Slf4j
@RestController
@ConditionalWingsEnabled(abs = LogConf.Key$enable)
public class WebLogViewer extends LogViewer {

@Autowired
public WebLogViewer(SlardarMonitorProp prop) {
super(prop.getView());
}

@Operation(summary = "Alarm logs can be viewed in conjunction with alarm notifications when self-monitoring is enabled.", description = """
# Usage
Pass the log id to view the log.
## Params
* @param id - log id, max 2k caches in 36H
## Returns
* @return {200 | string} log context or empty""")
@GetMapping(value = "${" + LogConf.Key$mapping + "}")
public void view(@RequestParam("id") String id, HttpServletResponse res) throws IOException {
super.view(id, res.getOutputStream());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;

import static pro.fessional.wings.silencer.datetime.DateTimePattern.FMT_FULL_19Z;
Expand All @@ -28,6 +29,7 @@ public class DingTalkReport implements WarnReport {

private final String dingConfig;
private final DingTalkNotice dingTalkNotice;
private final AtomicLong counter = new AtomicLong(1);

protected String gitInfo = null;

Expand Down Expand Up @@ -76,7 +78,7 @@ public Sts report(String appName, String jvmName, Map<String, List<WarnMetric.Wa
}
});

String subject = appName;
String subject = counter.getAndIncrement() + " " + appName;
if (StringUtils.isNotEmpty(conf.getNoticeKeyword())) {
subject = subject + " " + conf.getNoticeKeyword();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

/**
* @author trydofor
Expand Down Expand Up @@ -55,6 +56,14 @@ public class LogConf {
private String domain = "";
public static final String Key$domain = Key + ".domain";

/**
* regexp of section header, e.g. `2023-02-04T11:09:32.692+08:00`, `2024-07-23 01:31:59.063`
*
* @see #Key$header
*/
private Pattern header = null;
public static final String Key$header = Key + ".header";

/**
* ignored alert string in logs.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,66 +1,44 @@
package pro.fessional.wings.slardar.monitor.viewer;

import io.swagger.v3.oas.annotations.Operation;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.cache2k.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import pro.fessional.mirana.id.Ulid;
import pro.fessional.wings.silencer.spring.boot.ConditionalWingsEnabled;
import pro.fessional.wings.slardar.cache.cache2k.WingsCache2k;
import pro.fessional.wings.slardar.monitor.WarnFilter;
import pro.fessional.wings.slardar.monitor.WarnMetric;
import pro.fessional.wings.slardar.spring.prop.SlardarMonitorProp;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
* @author trydofor
* @since 2021-07-20
*/
@Slf4j
@RestController
@ConditionalWingsEnabled(abs = LogConf.Key$enable)
public class LogViewer implements WarnFilter {

@Getter
private final LogConf conf;
private final Cache<String, String> cache;

@Autowired
public LogViewer(SlardarMonitorProp prop) {
this(prop.getView());
}

public LogViewer(LogConf conf) {
this.conf = conf;
this.cache = WingsCache2k.builder(LogViewer.class, "cache", 2_000, conf.getAlive(), null, String.class, String.class).build();
}

@Operation(summary = "Alarm logs can be viewed in conjunction with alarm notifications when self-monitoring is enabled.", description = """
# Usage
Pass the log id to view the log.
## Params
* @param id - log id, max 2k caches in 36H
## Returns
* @return {200 | string} log context or empty""")
@GetMapping(value = "${" + LogConf.Key$mapping + "}")
public void view(@RequestParam("id") String id, HttpServletResponse res) throws IOException {
public void view(String id, OutputStream output) throws IOException {
if (id == null) return;
final String log = cache.get(id);
if (log == null) return;
Expand All @@ -69,11 +47,10 @@ public void view(@RequestParam("id") String id, HttpServletResponse res) throws

try (FileInputStream fis = new FileInputStream(file)) {
final long len = conf.getLength().toBytes();
final ServletOutputStream outputStream = res.getOutputStream();
IOUtils.copyLarge(fis, res.getOutputStream(), 0L, len);
IOUtils.copyLarge(fis, output, 0L, len);
if (file.length() - len > 0) {
final String more = String.format("\n\n...... %,d / %,d bytes", len, file.length());
outputStream.write(more.getBytes());
output.write(more.getBytes());
}
}
}
Expand Down Expand Up @@ -116,30 +93,32 @@ public void filter(Map<String, List<WarnMetric.Warn>> warns) {
}
}

private boolean canIgnoreHead(String out) {
if (conf.getIgnore().isEmpty()) return false;
protected boolean canIgnoreHead(String out) {
final Collection<String> ignores = conf.getIgnore().values();
if (ignores.isEmpty()) return false;

long max = conf.getLength().toBytes();
final File file = new File(out);
if (file.length() > max || !file.canRead()) return false;

final Pattern head = conf.getHeader();
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
final Collection<String> ign = conf.getIgnore().values();
String line;
int tol = 0;
int cnt = 0;
out:
while ((line = reader.readLine()) != null && max > 0) {
if (line.isEmpty()) {
max -= line.length() + 1; // loose calculation

if (line.isEmpty() || (head != null && !head.matcher(line).find())) {
continue;
}
//
max -= line.length(); // loose calculation

// only match header line
tol++;
for (String s : ign) {
for (String s : ignores) {
if (line.contains(s)) {
cnt++;
continue out;
break;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ wings.slardar.monitor.view.alive=36H
wings.slardar.monitor.view.length=1MB
## host or ip for external access.
wings.slardar.monitor.view.domain=http://${server.address:localhost}:${server.port:80}
## regexp of section header, e.g. `2023-02-04T11:09:32.692+08:00`, `2024-07-23 01:31:59.063`
wings.slardar.monitor.view.header=^\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}[-+.:0-9]*\\s+

## ignored alert string in logs.
## kotlin is support, but not really used
Expand All @@ -81,8 +83,8 @@ wings.slardar.monitor.view.ignore[AutoLog-Switch]=Auto Switch the following Appe
wings.slardar.monitor.view.ignore[No-MessageSource]=not found for MessageSource
## PersistenceProviderResolverHolder, Using jooq can logging.level.javax.persistence.spi=ERROR
wings.slardar.monitor.view.ignore[Jpa-Persistence]=.persistence.spi::No valid providers found
## UT005071: Undertow request failed HttpServerExchange{ CONNECT ; CONNECT
wings.slardar.monitor.view.ignore[UT005071-CONNECT]=UT005071: Undertow request failed HttpServerExchange{ CONNECT
## UT005071: Undertow request failed HttpServerExchange{ CONNECT
wings.slardar.monitor.view.ignore[UT005071-CONNECT]=UT005071: Undertow request failed HttpServerExchange
wings.slardar.monitor.view.ignore[Spring-WebIgnore]=You are asking Spring Security to ignore

## use DingTalk bot by default with the key `monitor`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package pro.fessional.wings.slardar.monitor.viewer;

import io.qameta.allure.TmsLink;
import lombok.Setter;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import pro.fessional.wings.slardar.spring.prop.SlardarMonitorProp;

import java.nio.file.Files;
import java.nio.file.Path;

/**
* @author trydofor
* @since 2024-07-23
*/
@SpringBootTest
class LogViewerTest {

@Setter(onMethod_ = { @Autowired })
protected SlardarMonitorProp slardarMonitorProp;

@TmsLink("13128")
@Test
void canIgnoreHead() throws Exception {
final LogViewer lv = new LogViewer(slardarMonitorProp.getView());

final Path tmp0 = Files.createTempFile("test-", null);
tmp0.toFile().deleteOnExit();
Files.writeString(tmp0, """
2024-07-21 22:05:22.957 ERROR 10884 --- [kite-front] [XNIO-1 I/O-4] io.undertow.request : UT005071: Undertow request failed HttpServerExchange{ CONNECT api.ipify.org:443}
java.lang.IllegalArgumentException: UT000068: Servlet path match failed
at io.undertow.servlet.handlers.ServletPathMatchesData.getServletHandlerByPath(ServletPathMatchesData.java:83) ~[undertow-servlet-2.3.10.Final.jar!/:2.3.10.Final]
""".stripIndent());


boolean b0 = lv.canIgnoreHead(tmp0.toAbsolutePath().toString());
Assertions.assertTrue(b0);

final Path tmp1 = Files.createTempFile("test-", null);
tmp1.toFile().deleteOnExit();
Files.writeString(tmp1, """
2024-07-21 22:05:22.957 ERROR 10884 --- [kite-front] [XNIO-1 I/O-4] io.undertow.request : UT005071: Undertow request failed HttpServerExchange{ CONNECT api.ipify.org:443}
java.lang.IllegalArgumentException: UT000068: Servlet path match failed
at io.undertow.servlet.handlers.ServletPathMatchesData.getServletHandlerByPath(ServletPathMatchesData.java:83) ~[undertow-servlet-2.3.10.Final.jar!/:2.3.10.Final]
at io.undertow.servlet.handlers.ServletPathMatches.getServletHandlerByPath(ServletPathMatches.java:133) ~[undertow-servlet-2.3.10.Final.jar!/:2.3.10.Final]
2024-07-23 01:33:05.446 ERROR 10884 --- [kite-front] [XNIO-1 task-4] p.f.w.w.e.DefaultExceptionResolver : unhandled exception, response default
java.lang.NumberFormatException: For input string
""".stripIndent());


boolean b1 = lv.canIgnoreHead(tmp1.toAbsolutePath().toString());
Assertions.assertFalse(b1);
}
}

0 comments on commit bfcf663

Please sign in to comment.