Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions data/completion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ complete:
opts:
- name: javagcjoin=
desc: If true it will return an array with each processed line.
- name: in=javathread
desc: A Java thread dump text file format or java pid
opts:
- name: javathreadpid=
desc: The PID of the Java process to connect to -requires Java SDK-
- name: in=jmx
desc: Uses Java JMX to retrieve data from another Java process
opts:
Expand Down
11 changes: 11 additions & 0 deletions data/usage.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@
"Input type": "javagc",
"Description": "The Java GC log lines text format"
},
{
"Input type": "javathread",
"Description": "The Java Thread stack dump lines text format"
},
{
"Input type": "jmx",
"Description": "Uses Java JMX to retrieve data from another Java process"
Expand Down Expand Up @@ -703,6 +707,13 @@
"Description": "If true it will return an array with each processed line."
}
],
[
{
"Option": "javathreadpid",
"Type": "Number",
"Description": "Optional you can provider the local java process pid to try to get the thread stack trace (*)"
}
],
[
{
"Option": "jmxpid",
Expand Down
15 changes: 15 additions & 0 deletions src/docs/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ List of data input types that can be auto-detected (through the file extension o
| ini | INI/Properties format |
| javas | Tries to list java processes running locally (javainception=true to include itself) |
| javagc | The Java GC log lines text format |
| javathread | The Java Thread stack dump lines text format |
| jmx | Uses Java JMX to retrieve data from another Java process |
| json | A JSON format (auto-detected) |
| jsonschema | Given a JSON schema format tries to generate sample data for it |
Expand Down Expand Up @@ -257,6 +258,20 @@ List of options to use when _in=javagc_:

---

### 🧾 JavaThread input options

List of options to use when _in=javathread_:

| Option | Type | Description |
|--------|------|-------------|
| javathreadpid | Number | Optional you can provider the local java process pid to try to get the thread stack trace (*) |

> (*) This requires running openaf/oafp with a Java JDK. Keep in mind that it will interrupt the target application to dump the necessary data.

> You can extract the input text data by executing ```kill -3 pid```

---

### 🧾 JMX input options

List of options to use when _in=jmx_:
Expand Down
104 changes: 104 additions & 0 deletions src/include/inputFns.js
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,110 @@ var _inputFns = new Map([
}
_$o(_r, options)
}],
["javathread", (_res, options) => {
var lines
_showTmpMsg()
if (isDef(params.javathreadpid)) {
ow.loadJava()
try {
lines = ow.java.jcmd(params.javathreadpid, "Thread.print")
lines = lines.split("\n").filter(l => l.startsWith("\""))
} catch(e) {
_exit(-1, "Error getting Java thread dump: " + e.message)
}
} else {
if (isString(_res)) {
lines = _res.split("\n")
} else {
_exit(-1, "javathreads is only supported with a raw input or javathreadpid=.")
}
}

// TODO: remove after OpenAF stable > 20240212
var fnFromTimeAbbr = aStr => {
_$(aStr, "aStr").isString().$_()

var ars = aStr.trim().match(/[\d\.]+[a-zA-Z]+/g), res = 0;
if (!isArray(ars) || ars.length === 0) return parseFloat(aStr);
for (var i in ars) {
var ar = ars[i].match(/(\d+(?:\.\d+)?)\s*([a-zA-Z]+)/);
if (isArray(ar) && ar.length > 0) {
var v = Number(ar[1])
var u = String(ar[2])

var _u = {
"ms": 1,
"s": 1000,
"m": 60 * 1000,
"h": 60 * 60 * 1000,
"d": 24 * 60 * 60 * 1000,
"w": 7 * 24 * 60 * 60 * 1000,
"M": 30 * 24 * 60 * 60 * 1000,
"y": 365 * 24 * 60 * 60 * 1000
}
if (isDef(_u[u])) {
res += v * _u[u]
} else {
res += v
}
}
}

return res
}

var fnJavaTrans = (v, tA) => {
if (v === null) return ""
if (v === undefined) return ""
if (isBoolean(v)) return Boolean(v)
if (isNumber(v)) return Number(v)
if (tA) return fnFromTimeAbbr(String(v))
return String(v)
}
ow.loadFormat()

var _r = []
lines.forEach(line => {
if (line.startsWith("\"")) {
var pt = java.util.regex.Pattern.compile("^\\\"(?<threadName>[^\"]+)\\\"" +
"(?:\\s+#(?<threadId>\\d+))?" +
"(?:\\s+\\[(?<threadIndex>\\d+)\\])?" +
"(?:\\s+(?<daemon>daemon))?" +
"(?:\\s+prio=(?<prio>\\d+))?" +
"\\s+os_prio=(?<osPrio>\\d+)" +
"(?:\\s+cpu=(?<cpu>[0-9.]+ms))?" +
"(?:\\s+elapsed=(?<elapsed>[0-9.]+s))?" +
"(?:\\s+tid=(?<tid>0x[a-fA-F0-9]+))?" +
"(?:\\s+nid=(?<nid>0x[a-fA-F0-9]+|\\d+|\\S+))?" +
"(?:\\s+(?<state>.*?))?" +
"(?:\\s+\\[(?<address>[^\\]]+)\\])?" +
"\\s*$")

var mt = pt.matcher(line)
if (mt.find()) {
var m = {
threadGroup: fnJavaTrans(mt.group("threadName")).replace(/[^a-zA-z]?\d+$/, ""),
threadName : fnJavaTrans(mt.group("threadName")),
threadId : fnJavaTrans(mt.group("threadId")),
threadIndex: fnJavaTrans(mt.group("threadIndex")),
daemon : fnJavaTrans(mt.group("daemon")),
prio : fnJavaTrans(mt.group("prio")),
osPrio : fnJavaTrans(mt.group("osPrio")),
cpu_ms : fnJavaTrans(mt.group("cpu"), true),
elapsed_ms : fnJavaTrans(mt.group("elapsed"), true),
tid : fnJavaTrans(mt.group("tid")),
nid : fnJavaTrans(mt.group("nid")),
state : fnJavaTrans(mt.group("state")),
address : fnJavaTrans(mt.group("address"))
}
_r.push(m)
} else {
_r.push({ error: "Could not parse line: " + line })
}
}
})
_$o(_r, options)
}],
["javagc", (_res, options) => {
if (!isBoolean(params.javagcjoin)) params.javagcjoin = toBoolean(_$(params.javagcjoin, "javagcjoin").isString().default(__))

Expand Down
9 changes: 7 additions & 2 deletions src/include/transformFns.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,14 +196,19 @@ var _transformFns = {
},
"correcttypes" : _r => {
if (toBoolean(params.correcttypes) && isObject(_r)) {
ow.loadFormat()
traverse(_r, (aK, aV, aP, aO) => {
switch(descType(aV)) {
case "number": if (isString(aV)) aO[aK] = Number(aV); break
case "string":
// String boolean
if (aV.trim().toLowerCase() == "true" || aV.trim().toLowerCase() == "false") { aO[aK] = toBoolean(aV); break }
// String ISO date
if (aV.trim().match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/)) { aO[aK] = new Date(aV); break }
if (isDef(ow.format.fromISODate)) {
if (aV.trim().match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\d+Z$/)) { aO[aK] = ow.format.fromISODate(aV); break }
} else {
if (aV.trim().match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/)) { aO[aK] = new Date(aV); break }
}
// String date
if (aV.trim().match(/^\d{4}-\d{2}-\d{2}$/)) { aO[aK] = new Date(aV); break }
// String time with seconds
Expand Down Expand Up @@ -557,7 +562,7 @@ var _transformFns = {
let _lst = params.field2date.split(",").map(r => r.trim())
traverse(_r, (aK, aV, aP, aO) => {
if (_lst.indexOf(aP.length > 0 && !aP.startsWith("[") ? aP.substring(1) + "." + aK : aK) >= 0 && isNumber(aV) && aV > 0) {
try { aO[aK] = new Date(aV) } catch(e) {}
try { aO[aK] = ow.format.fromISODate(aV) } catch(e) {}
}
})
return _r
Expand Down
Loading