Das Java Observability Toolkit (JOT) ist eine Plattform, um jede Java-Anwendung beobachtbar zu machen, ohne Code zu schreiben oder gar neu zu kompilieren. Die Plattform besteht aus...
Sie können JOTs schnell und einfach ohne Programmieraufwand erstellen. JOTs können nahezu alles in einer laufenden Java-Anwendung beobachtbar machen. Hier sind ein paar Beispiele dafür, wofür Sie es verwenden könnten ... aber die einzige Grenze ist Ihre Kreativität.
Sie stecken bei der Behebung eines Produktionsproblems fest und verfügen nicht über genügend Protokolle oder die Möglichkeit zum Debuggen. Mit einer einfachen JOT-Regel können Sie schnell und einfach an die benötigten Daten gelangen.
Sie möchten etwas überwachen, auf das nur tief in einer App zugegriffen werden kann, und Sie haben keine Möglichkeit, es Ihren Tools zugänglich zu machen.
Sie möchten den Missbrauch leistungsstarker Methoden durch Angreifer verhindern, diese befinden sich jedoch in Bibliotheken, die Sie nicht kontrollieren.
Das Ziel von JOT besteht darin, maximale Instrumentierungsleistung bereitzustellen, ohne Kompromisse bei der Einfachheit einzugehen.
Teilen Sie uns die coolen Dinge mit, die Sie mit JOT machen! Und erwägen Sie, sie wieder in das Projekt einzubringen, damit andere an Ihrer Großartigkeit teilhaben können. Der einfachste Weg, einen Beitrag zu leisten, besteht darin, eine Pull-Anfrage zu erstellen. Wenn Sie ein JOT teilen, fügen Sie es dem „contrib“-Verzeichnis des Projekts hinzu ... und fügen Sie bitte einen Kommentar hinzu, der detailliert beschreibt, wofür es verwendet werden soll.
Definieren Sie Ihre Sensoren und Berichte in Yaml und speichern Sie sie in einer .jot-Datei. Sensoren definieren, welche Daten erfasst werden sollen, und „Berichte“ teilen JOT mit, wie die Daten im Zeitverlauf verfolgt werden sollen.
sensors:
- name: "get-ciphers"
description: "Identifies encryption ciphers"
methods:
- "javax.crypto.Cipher.getInstance"
captures:
- "#P0"
reports:
- name: "Encryption Usage"
type: "list"
cols: "get-ciphers"
Im Grunde müssen Sie lediglich JOT zur JVM hinzufügen
Dann verwenden Sie einfach Ihre Anwendung wie gewohnt und lassen JOT Daten für Sie sammeln. JOT erstellt eine schöne Tabelle, in der genau erfasst ist, wo die Verschlüsselung verwendet wird und welcher Algorithmus angegeben ist.
Encryption Algorithms get-ciphers
------------------------------------------------------------ ----------------------
com.acme.ticketbook.Ticket.encrypt(Ticket.java:125) DES
org.apache.jsp.accessA_jsp._jspService(accessA_jsp.java:212) AES
org.apache.jsp.accessA_jsp._jspService(accessA_jsp.java:213) PBEWithMD5AndTripleDES
org.apache.jsp.accessB_jsp._jspService(accessB_jsp.java:212) DES
org.apache.jsp.accessC_jsp._jspService(accessC_jsp.java:212) DES/CBC/PKCS5Padding
Möglicherweise ist die Verwendung der Umgebungsvariablen JAVA_TOOL_OPTIONS hilfreich. Wie „export JAVA_TOOL_OPTIONS="-javaagent:jot.jar=ciphers.jot". Dann wird Java unabhängig davon, wie es letztendlich gestartet wird, JOT verwenden.
Wenn Sie mehrere JOTs gleichzeitig verwenden möchten, können Sie sie entweder alle in einer großen .jot-Datei ablegen oder Sie können mehrere .jot-Dateien in einem Verzeichnis ablegen und „-javaagent:jot.jar=directory“ verwenden.
Schließlich kann jedes JOT mehrere „Erfassungen“ haben. Eine Erfassung ist eine Möglichkeit, anzugeben, welche Daten Sie mit den im JOT angegebenen Methoden erfassen möchten. Die einfachen Erfassungen sind Dinge wie: #P0 – der erste Parameter #P1 – der zweite Parameter #ARGS – alle in einem String verketteten Parameter #OBJ – das Objekt, bei dem die Methode aufgerufen wird #RET – die Rückgabe der Methode
Captures sind eigentlich SpEL-Ausdrücke (Spring Expression Language), sodass Sie Methoden für diese Basisobjekte aufrufen, Dinge vergleichen und Operationen ausführen können. Dies hilft Ihnen, genau das zu beobachten, was Sie wollen. Nachfolgend finden Sie alle Einzelheiten zum Schreiben Ihrer eigenen Sensoren.
JOT verwendet FluentLogger, der die Konfiguration von java.util.logging
übernimmt und von der JVM mit -Djava.util.logging.config.file
konfiguriert werden kann
Diese Beispielkonfiguration protokolliert JOT sowohl in stdout als auch in /tmp/jot.log
handlers = java.util.logging.ConsoleHandler, java.util.logging.FileHandler
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format = %1$tF %1$tT %4$-7s [%2$s] - %5$s %n
java.util.logging.FileHandler.level=ALL
java.util.logging.FileHandler.pattern=/tmp/jot.log
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.FileHandler.append=true
mit:
java -javaagent:jot.jar=ciphers.jot -Djava.util.logging.config.file=/root/jul.properties
Mithilfe von Sensoren definieren Sie die Daten, die Sie sammeln möchten. Sie können Sensoren auch verwenden, um begrenzte Aktionen innerhalb einer Anwendung auszuführen.
# The name of this sensor, which can be referenced by reports
- name: "get-ciphers"
# Use this to describe what this sensor does. Try to provide enough
# detail so that anyone would understand what it's about.
description: "What does sensor do?"
# A list of methods to gather data from. To avoid potential performance issues,
# avoid putting sensors in methods that are extremely frequently used,
# like StringBuffer.append() or File.<init>.
methods:
- "a.b.c.Class.method"
- "d.e.f.Class.method"
# Scopes allow you to limit when a sensor will fire.
# A sensor will fire if it is invoked while "inside" one of the scopes. That is,
# when any of these methods is on the stack.
# You can define negative scopes using !scope, so that this sensor will only fire outside the scope
# For static methods you need to prefix the method with "static"
scopes:
- "a.b.c.Class.method" # matches only if inside method
- "!a.b.c.Class.method" # matches only if NOT inside method
- "static a.b.c.Class.method" # matches if inside static method
# Excludes allow you to prevent data from being gathered from any
# classes that starts with any of these patterns or are "isAssignableFrom" these classes
# FIXME: currently you must put .all after each classname
excludes:
- "javax.el.MapELResolver.all"
- "org.foo.package.all"
# Captures are the workhorse of JOT and are written in Spring Expression Language (SpEL)
# You may reference data from the method currently running.
# Options are OBJ, P1..P10, ARGS, RET, STACK. These objects are whatever type they happen
# to be, and you can invoke any existing methods on those objects. Note that STACK is a StackFrame[]
# See https://blog.abelotech.com/posts/useful-things-spring-expression-language-spel/
captures:
- "#RET?.toUpperCase()" # call toUpperCase if #RET is not null (note ?. operator)
- """+#P0+":"+#RET" # for methods that take a name and return a value
- "#OBJ.getCanonicalPath() + " " + #OBJ.exists() ? [EXISTS] : [DOES NOT EXIST]" # use ternary operator
- """+#P0+":"+(#RET ? "Y" : "N")" # for methods that return boolean
# Matchers allow you to filter the data that was captured with a set of regular expressions.
# If there are no matchers then all captures will report data.
# Positive matchers only fire if the data matches the pattern. You'll get a result if any positive matchers match.
# Negative matchers (starting with !) only fire if the data does not match the pattern. You'll get a result if no negative matchers match.
# If you mix positive and negative, you'll get a result if any positive captures match and no negative matchers match.
matchers:
- "^\w*$" # matches anything with word characters start to finish
- "!^\[foo" # matches anything that doesn't start with foo
- "!null" # hide empty results from output
# Exceptions are a way to change the behavior of an application.
# If the sensor fires (capture occurs and matchers fire) then JOT will
# throw a SensorException.
# The message should be appropriate for end user, JOT will log all the relevant details.
# Note that generally you don't want to use #RET in your capture if you're throwing an exception,
# because it will throw at the end of the method, which is probably too late.
exception: "Message"
# Debug mode will generate extra logging for this rule only
debug: "false"
Mit Berichten können Sie definieren, wie JOT im Laufe der Zeit Daten sammelt und wie es sie für Sie formatiert.
# Title of this report that will be displayed above the results
- name: "example"
# Type of reports include
# 1. list
# ROWS: caller method
# COLS: one column named after the sensor defined in "cols"
# DATA: values from the sensor named in "cols"
# 2. compare
# ROWS:
# COLS: uses the "cols" sensor values as column headers
# DATA: values from the sensor named in "cols"
# 3. table
# ROWS:
# COLS: uses rule names for cols[0-n] as column headers - parses data values
# DATA: values from the sensor named in "cols"
# 4. series - table but each row starts with a timestamp (currently includes callers col too)
# ROWS:
# COLS: uses rule names for cols[0-n] as column headers - parses data values
# DATA: values from the sensor named in "cols"
type: "table"
# Rows indicates a sensor to be used to populate row headers
rows: "get-routes"
# Cols indicates a sensor (or list of sensors) to be used to populate column headers
cols: "get-users"
# Data indicates how the content of data cells should be populated. Can be a fixed string or a rule name.
data: "X"
Sollte so einfach sein wie das Klonen dieses Repos und das Erstellen mit Maven
$ git clone https://github.com/planetlevel/jot.git
$ cd jot
$ mvn install
Dann können Sie das jot-xxjar im Zielverzeichnis verwenden.
Beiträge sind willkommen. Sehen Sie sich den Bugtracker an, um Probleme zu finden, an denen Sie arbeiten können, wenn Sie JOT verbessern möchten.
- Solve problem of sensors inside JOT scope
1) don't instrument JOT classes -- anything using shading
2) use global scope to check anywhere you're inside a sensor call
- Create separate JOT log file instead of using java.util.logging
- New rules
1) which routes are non-idempotent?
- Sensors
# future features - maybe think about reporting?
# enabled: "false"
# sample: "1000" # report every 1000 times? time frequency?
# counter: "?" # report 10 mins?
# scope: include a capture and regex to say whether to start scope (if service.P0.getParameter=foobar)
# exec: run this code. before? after? during?
- Reports
# possible additions to cols -- caller, stack[n], trace#
- Query Language
# investigate using query language instead of yaml jots.
# Perhaps two types of "queries" -- one to create sensors, another to pull reports.
# SELECT #P0.toUpperCase()
FROM java.lang.Runtime.getRuntime.exec
WITHIN javax.servlet.Servlet.service
WHERE pattern
# SELECT [capture, capture]
FROM [method, method]
EXCLUDE [class, class]
WITHIN [scope, scope]
WHERE [pattern, pattern]
THROWS [msg]
# SELECT #P0 FROM javax.crypto.Cipher.getInstance
# SELECT #P0 FROM javax.crypto.Cipher.getInstance
WHERE "DES|DESEDE"
THROWS "Weak encryption algorithm detected"