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 を同時に使用する場合は、それらをすべて 1 つの大きな .jot ファイルに入れるか、複数の .jot ファイルを 1 つのディレクトリに入れて「-javaagent:jot.jar=directory」を使用することができます。
最後に、すべての JOT は複数の「キャプチャ」を持つことができます。キャプチャは、JOT で指定したメソッドから収集するデータを指定する方法です。単純なキャプチャは次のようなものです。 #P0 - 最初のパラメータ #P1 - 2 番目のパラメータ #ARGS - 文字列に連結されたすべてのパラメータ #OBJ - メソッドが呼び出されるオブジェクト #RET - メソッドからの戻り値
キャプチャは実際には Spring Expression Language (SpEL) 式なので、これらの基本オブジェクトのメソッドを呼び出し、比較し、操作を行うことができます。これは、必要なものを正確に観察するのに役立ちます。独自のセンサーの作成の詳細については、以下を参照してください。
JOT は、 java.util.logging
から構成を取得する FluentLogger を使用し、 -Djava.util.logging.config.file
を使用して JVM によって構成できます。
このサンプル構成は、JOT を stdout と /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"