Java Observability Toolkit (JOT) — это платформа, позволяющая сделать любое Java-приложение доступным для наблюдения без написания кода или даже перекомпиляции. Платформа состоит из...
Вы можете быстро и легко создавать JOT без какого-либо кодирования. JOT могут сделать наблюдаемым практически все в работающем Java-приложении. Вот несколько примеров того, для чего вы можете его использовать... но единственным ограничением является ваше творчество.
Вы застряли в устранении производственной проблемы, и у вас недостаточно журналов или возможностей для отладки. С помощью простого правила JOT вы можете быстро и легко получить необходимые данные.
Вы хотите отслеживать что-то, что доступно только глубоко внутри приложения, и у вас нет возможности предоставить доступ к этому вашим инструментам.
Вы хотите предотвратить злоупотребление злоумышленниками мощными методами, но они находятся в библиотеках, которые вы не контролируете.
Цель JOT — обеспечить максимальную мощность приборов без ущерба для простоты.
Расскажите нам, какие крутые вещи вы делаете с JOT! И подумайте о том, чтобы вернуть их в проект, чтобы другие могли поделиться вашими замечательными идеями. Самый простой способ внести свой вклад — создать запрос на включение. Если вы делитесь JOT, добавьте его в каталог «contrib» проектов... и добавьте комментарий с подробным описанием того, для чего он предназначен.
Определите свои датчики и отчеты в формате yaml и сохраните их в файле .jot. Датчики определяют, какие данные собирать, а «отчеты» сообщают JOT, как отслеживать данные с течением времени.
sensors:
- name: "get-ciphers"
description: "Identifies encryption ciphers"
methods:
- "javax.crypto.Cipher.getInstance"
captures:
- "#P0"
reports:
- name: "Encryption Usage"
type: "list"
cols: "get-ciphers"
По сути, все, что вам нужно сделать, это добавить JOT в JVM.
Затем вы просто используете свое приложение в обычном режиме и позволяете JOT собирать данные для вас. JOT создаст красивую таблицу, в которой будет точно указано, где используется шифрование и какой алгоритм указан.
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
Возможно, вам будет полезно использовать переменную среды JAVA_TOOL_OPTIONS. Например, «export JAVA_TOOL_OPTIONS="-javaagent:jot.jar=ciphers.jot». Тогда независимо от того, как Java в конечном итоге будет запущена, она будет использовать JOT.
Если вы хотите использовать несколько файлов JOT одновременно, вы можете либо поместить их все в один большой файл .jot, либо поместить несколько файлов .jot в каталог и использовать «-javaagent:jot.jar=directory».
Наконец, каждый JOT может иметь несколько «захватов». Сбор — это способ указать, какие данные вы хотите собрать с помощью методов, указанных в JOT. Простые захваты — это такие вещи, как: #P0 — первый параметр #P1 — второй параметр #ARGS — все параметры, объединенные в строку #OBJ — объект, для которого вызывается метод #RET — возврат из метода
На самом деле захваты представляют собой выражения Spring Expression Language (SpEL), поэтому вы можете вызывать методы этих базовых объектов, сравнивать данные и выполнять операции. Это поможет вам наблюдать именно то, что вы хотите. Все подробности написания собственных датчиков смотрите ниже.
JOT использует FluentLogger, который берет конфигурацию из java.util.logging
и может быть настроен JVM с помощью -Djava.util.logging.config.file
В этом примере конфигурации JOT будет записываться в стандартный вывод, а также в /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
с использованием:
java -javaagent:jot.jar=ciphers.jot -Djava.util.logging.config.file=/root/jul.properties
Датчики – это то, как вы определяете данные, которые хотите собрать. Вы также можете использовать датчики для выполнения ограниченных действий внутри приложения.
# 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"
Отчеты позволяют вам определить, как JOT будет собирать данные с течением времени и как он будет их форматировать.
# 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"
Должно быть так же просто, как клонировать этот репозиторий и построить его с помощью maven.
$ git clone https://github.com/planetlevel/jot.git
$ cd jot
$ mvn install
Затем вы можете использовать jot-xxjar в целевом каталоге.
Взносы приветствуются. Просмотрите систему отслеживания ошибок, чтобы найти проблемы, над которыми стоит поработать, если вы хотите улучшить JOT.
- 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"