{"id":26659873,"url":"https://github.com/ashutoshiwnl/monarchjavaagent","last_synced_at":"2026-05-22T06:15:45.540Z","repository":{"id":282572037,"uuid":"949017530","full_name":"AshutoshIWNL/MonarchJavaAgent","owner":"AshutoshIWNL","description":"Monarch-Java-Agent is a powerful Java agent for method monitoring and analysis. It offers various features to track method execution time, print stack traces, log method arguments and return values, capture heap dumps, and gather JVM-related information.","archived":false,"fork":false,"pushed_at":"2025-03-15T14:20:53.000Z","size":0,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-15T14:33:40.623Z","etag":null,"topics":["application-monitoring","bytecode-manipulation","instrumentation-agent","java-agent"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AshutoshIWNL.png","metadata":{"files":{"readme":"README.MD","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-03-15T13:44:34.000Z","updated_at":"2025-03-15T14:20:56.000Z","dependencies_parsed_at":"2025-03-15T19:15:11.781Z","dependency_job_id":null,"html_url":"https://github.com/AshutoshIWNL/MonarchJavaAgent","commit_stats":null,"previous_names":["ashutoshiwnl/monarchjavaagent"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AshutoshIWNL%2FMonarchJavaAgent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AshutoshIWNL%2FMonarchJavaAgent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AshutoshIWNL%2FMonarchJavaAgent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AshutoshIWNL%2FMonarchJavaAgent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AshutoshIWNL","download_url":"https://codeload.github.com/AshutoshIWNL/MonarchJavaAgent/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245449674,"owners_count":20617190,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["application-monitoring","bytecode-manipulation","instrumentation-agent","java-agent"],"created_at":"2025-03-25T11:15:51.621Z","updated_at":"2026-05-22T06:15:45.534Z","avatar_url":"https://github.com/AshutoshIWNL.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MonarchJavaAgent\n\n![Monarch Logo](jekyll/source/monarch-logo-v2.png)\n\nMonarchJavaAgent is a hybrid Java agent for production diagnostics and JVM observation. It lets you attach targeted bytecode instrumentation to a live JVM, expose Prometheus-compatible JVM metrics, and use runtime configuration to move between instrumenter, observer, and hybrid workflows without changing the application code.\n\nThis is useful when you need to:\n\n- inspect a hot method in production without redeploying\n- inject a temporary diagnostic probe to validate a runtime hypothesis\n- expose JVM health signals to Prometheus and Grafana\n- combine runtime traces with monitoring data during incident response\n\n## Features\n\n- **Stack Trace Printing**: Print the stack trace when a certain method is invoked. Supports conditional printing only if a specific class/method appears in the current stack trace.\n- **Return Value Logging**: Log the return value of a method.\n- **Custom code addition**: Add custom code to a method.\n- **Trace macro for custom code**: Use `MLOG(...)` inside `ADD` rules to route diagnostic output into Monarch trace logs instead of app stdout/app logger.\n- **Class replacement**: Replace already loaded classes using external bytecode from `.class` files or `.jar` entries via JVM redefine.\n- **Heap Dump Capture**: Take a heap dump when a method is invoked or exits.\n- **Method Execution Time**: Print the time taken for a method's execution (Bit buggy currently and needs bytecode verification to be turned off).\n- **System Flags Printing**: Print system flags of the target application.\n- **JVM Options Printing**: Print JVM options of the target application.\n- **JVM Heap Usage Details**: Print JVM heap usage details of the target application.\n- **JVM CPU Usage Details**: Print JVM CPU usage details of the target application.\n- **JVM GC Stats**: Print JVM Garbage Collection stats including collection count and time.\n- **JVM Thread Stats**: Print JVM thread stats including current thread count and peak thread count.\n- **JVM Classloader Stats**: Print JVM class loading stats including loaded/unloaded classes, class load rate, and Metaspace usage.\n- **Metrics HTTP Endpoint**: Exposes JVM metrics (heap, GC, threads, CPU, classloader) via a built-in `/metrics` endpoint in Prometheus text format, with OpenMetrics negotiation support and JSON compatibility at `/metrics.json`.\n- **Alert Emails**: Sends alert emails for high heap usage, high CPU usage, thread deadlock, high classloading, etc. in the target application. \n\n## Production Use Cases\n\n- Attach to a live JVM and trace method arguments, return values, or stack frames on a hot path.\n- Add a temporary runtime debug probe with `ADD` or `CODEPOINT` rules to confirm an application hypothesis.\n- Run in `observer` mode to expose JVM metrics to Prometheus without enabling bytecode instrumentation.\n- Run in `hybrid` mode to combine targeted instrumentation and scrapeable metrics during incident investigation.\n\n## Architecture (Current)\n\nAfter refactoring, startup wiring is split into focused bootstrap components:\n\n- `AgentConfigurator`: thin facade used by `Agent.premain` and `Agent.agentmain`.\n- `AgentStartupOrchestrator`: startup sequence orchestration.\n- `LoggingBootstrap`, `ConfigBootstrap`, `TraceBootstrap`, `SmtpBootstrap`, `TransformerBootstrap`, `InstrumentationManagerBootstrap`: focused startup stages.\n\nThis keeps behavior unchanged while reducing coupling in the startup path.\n\nTransformer internals are also split using a handler strategy:\n\n- `GlobalTransformer`: orchestration (rule selection, backup, dispatch)\n- `ActionExecution`: per-rule execution context\n- `transformer.handlers.*`: isolated action handlers (`ARGS`, `RET`, `STACK`, `HEAP`, `ADD`, `PROFILE`)\n\n## Agent Arguments\n\n| Argument          | Description                                                               |\n|-------------------|---------------------------------------------------------------------------|\n| `configFile`      | Path to the configuration file specifying agent behavior **[Mandatory]**. |\n| `agentLogFileDir` | Directory where initialization logs will be written.                      |\n| `agentLogLevel`   | Log verbosity level (`DEBUG`, `INFO`, `WARN`, `ERROR`).                   |\n| `smtpProperties`  | Path to SMTP configuration for sending alert emails.                      |\n| `agentJarPath`    | Path to the MonarchJavaAgent jar **[Mandatory for startup attach]**.      |\n\n\n## Usage\n\nYou can attach MonarchJavaAgent either during startup or during runtime.\n\n**For startup:**\n\n1. Download the latest agent JAR file from releases page.\n2. Start your Java application using the `-javaagent` option.\n3. Specify the configuration file and other options as needed.\n\nExample command to attach the agent:\n\n```bash \njava -Xverify:none -javaagent:/path/to/MonarchJavaAgent.jar=configFile=/path/to/config.yaml,agentLogFileDir=/path/to/log/dir,agentLogLevel=DEBUG,smtpProperties=/path/to/smtpProperties.props,agentJarPath=/path/to/MonarchJavaAgent.jar YourMainClass\n```\n\n**For Runtime:**\n\n1. Download the latest agent JAR file from releases page.\n2. Start your application.\n3. Run \"monarchAgentStart.bat\"/\"monarchAgentStart.sh\" and provide the requested details.\n\nExample command run:\n```bash \nC:\\Users\\ashut\\monarch-java-agent\\attachScript\u003e .\\monarchAgentStart.bat\nEnter path to the agent JAR file: C:\\Users\\ashut\\monarch-java-agent\\target\\MonarchJavaAgent-1.2.jar\nEnter path to the agent config file: C:\\Users\\ashut\\monarch-java-agent\\sampleConfig\\mConfig.yaml\nEnter arguments to pass to the agent: agentLogFileDir=C:\\Users\\ashut\\manualTesting,agentLogLevel=DEBUG,smtpProperties=/path/to/smtpProperties.props\nEnter PID of the target JVM (press Enter to use current JVM): 24300\nAgent attached successfully to PID 24300\n```\n\nPreflight validation mode (no startup side effects):\n\n```bash\njava -javaagent:/path/to/MonarchJavaAgent.jar=configFile=/path/to/config.yaml,preflight=true,agentLogFileDir=/path/to/log/dir,agentLogLevel=INFO,agentJarPath=/path/to/MonarchJavaAgent.jar YourMainClass\n```\n\nWhen `preflight=true` is provided, Monarch validates config and rules, prints a validation summary, and exits without starting instrumentation/observer components.\n\n## Configuration\n\nMonarchJavaAgent now supports a nested, mode-aware YAML structure. Supported modes are:\n\n- `instrumenter`: enable bytecode instrumentation only\n- `observer`: enable JVM observation/metrics only\n- `hybrid`: enable both instrumentation and observer features\n\nCanonical sample configuration:\n\n```yaml\nmode: hybrid\n\ninstrumentation:\n  enabled: true\n  configRefreshInterval: 15\n  traceFileLocation: C:\\\\TraceFileDumps\n  agentRules:\n    - ClassA::methodA@INGRESS::STACK\n    - ClassA::methodA@INGRESS::ARGS\n    - ClassA::methodA@EGRESS::RET\n    - ClassA::methodB@INGRESS::ARGS\n    - ClassA::methodB@INGRESS::STACK::[com.asm]\n    - ClassA::methodB@EGRESS::STACK\n    - ClassA::methodB@EGRESS::RET\n    - ClassB::methodC@PROFILE\n    - ClassB::methodC@INGRESS::HEAP\n    - ClassB::methodC@INGRESS::ADD::[System.out.println(20);]\n    - ClassA::methodA@INGRESS::ADD::[System.out.println(this.getClass().getName());]\n    - ClassA::methodA@CODEPOINT(11)::ADD::[System.out.println(499);]\n    - ClassA::methodA@CODEPOINT(11)::ADD::[System.out.println(499 + \",\" + \"Ashutosh Mishra\");]\n    - com.example.MyService@CHANGE::FILE::[/opt/patches/MyService.class]\n    - com.example.*@CHANGE::JAR::[/opt/patches/hotfix.jar]\n\nobserver:\n  enabled: true\n  printClassLoaderTrace: true\n  printJVMSystemProperties: true\n  printEnvironmentVariables: true\n  metrics:\n    exposeHttp: true\n    port: 9090\n    heapUsage: true\n    cpuUsage: true\n    threadUsage: true\n    gcStats: true\n    classLoaderStats: true\n\nalerts:\n  enabled: true\n  maxHeapDumps: 3\n  emailRecipientList:\n    - abc@example.com\n    - ashutosh@asm.com\n```\n\nBackward compatibility:\n\n- Legacy flat keys such as `shouldInstrument`, `printJVMHeapUsage`, `exposeMetrics`, and `metricsPort` are still supported.\n- Legacy flat keys are deprecated and now emit warnings during config parsing.\n- If both nested and legacy forms are present for the same setting, the nested value wins.\n\nCurrent implementation note:\n\n- `instrumentation.traceFileLocation` is still used as the shared trace output root for both instrumentation and observer trace output.\n- For `observer` mode, set `instrumentation.traceFileLocation` as well until trace output is moved to a common/shared config section in a future cleanup.\n\nLaunch args remain separate from YAML:\n\n- `configFile`\n- `agentLogFileDir`\n- `agentLogLevel`\n- `smtpProperties`\n- `agentJarPath`\n\nSee the checked-in example at [sampleConfig/mConfig.yaml](sampleConfig/mConfig.yaml).\n\nLegacy flat example still accepted:\n\n```yaml\nshouldInstrument: true\nconfigRefreshInterval: 15\ntraceFileLocation: C:\\\\TraceFileDumps\nagentRules:\n  - ClassA::methodA@INGRESS::STACK\nprintJVMHeapUsage: true\nprintJVMCpuUsage: true\nprintJVMThreadUsage: true\nprintJVMGCStats: true\nprintJVMClassLoaderStats: true\nexposeMetrics: true\nmetricsPort: 9090\nmaxHeapDumps: 3\nsendAlertEmails: true\nemailRecipientList:\n  - abc@example.com\n```\n\n## Rule Syntax\n\nThe rule syntax for MonarchJavaAgent follows the format:\n\n```plaintext\n\u003cFQCN\u003e::\u003cMethodName\u003e@\u003cEVENT\u003e::\u003cACTION\u003e\n```\n\nWhere:\n\n- `\u003cFQCN\u003e`: Fully Qualified Class Name.\n- `\u003cMethodName\u003e`: Name of the method.\n- `\u003cEVENT\u003e`: Event at which the action should be performed. Possible values are:\n    - INGRESS\n    - EGRESS\n    - CODEPOINT\n    - PROFILE (Note: PROFILE is a special case and no ACTION is required along with it.)\n- `\u003cACTION\u003e`: Action to be performed. Possible values are:\n    - STACK: Print stack trace.\n    - HEAP: Capture heap dump.\n    - ARGS: Log method arguments.\n    - RET: Log method return value.\n    - ADD: Add custom code.\n\nOptional `ADD` trace macro:\n\n- `MLOG(\u003cexpr\u003e)`: Writes `String.valueOf(\u003cexpr\u003e)` to Monarch trace file via `TraceFileLogger`.\n- `MLOG(...)` is optional and backward-compatible; existing `ADD` payloads still work.\n\nExamples:\n\n```plaintext\nClassA::methodA@INGRESS::ADD::[System.out.println(\"legacy add path\");]\nClassA::methodA@INGRESS::ADD::[MLOG(\"Money: \" + objA.getMoney());]\nClassA::methodA@INGRESS::ADD::[if (objA != null) { MLOG(\"objA=\" + objA); }]\n```\n\nClass replacement rule syntax:\n\n```plaintext\n\u003cClassPattern\u003e@CHANGE::FILE::[\u003c/absolute/path/to/ClassName.class\u003e]\n\u003cClassPattern\u003e@CHANGE::JAR::[\u003c/absolute/path/to/patches.jar\u003e]\n```\n\nWhere:\n\n- `\u003cClassPattern\u003e` is either an exact FQCN (for example `com.example.MyClass`) or a wildcard suffix pattern (for example `com.example.*`).\n- `CHANGE` rules apply only to classes that are already loaded.\n- `FILE` uses raw bytes from the class file.\n- `JAR` resolves `\u003cClassPattern\u003e` matches to class entries inside the jar.\n\nRule validation diagnostics:\n\n- Startup now emits a rule validation summary with accepted/rejected/skipped counts.\n- Rejected rules include reason + suggestion.\n- `CHANGE::FILE` / `CHANGE::JAR` rules are prevalidated for path existence/readability and source-type expectations.\n\n## Metrics Endpoint\n\nIf `observer.metrics.exposeHttp: true` is enabled in the config, MonarchJavaAgent starts a lightweight HTTP server exposing JVM metrics.\n\nDefault endpoints:\n\n- `http://localhost:9090/metrics` (Prometheus text format by default)\n- `http://localhost:9090/metrics.json` (legacy JSON compatibility)\n\n`/metrics` supports OpenMetrics content negotiation. If the request includes:\n\n- `Accept: application/openmetrics-text; version=1.0.0`\n\nthe endpoint responds with OpenMetrics content type and EOF marker.\n\nExported metrics include:\n\n- Heap usage (bytes and usage percentages)\n- CPU load and cores\n- GC interval stats (per-GC labels) plus GC totals/rates for long-window analysis\n- Thread stats\n- Classloader stats\n- Agent info + scrape timestamp\n\nSampling cadence transparency:\n\n- Monarch updates metric values on monitor loops, not on every scrape.\n- Current monitor loop cadences are:\n  - CPU: every 10s\n  - Heap: every 30s\n  - GC / Thread / Classloader: every 60s\n- If Prometheus scrapes faster than these cadences (for example every 2s), repeated samples are expected between monitor updates. This is correct behavior, but adds little extra signal for those metrics.\n- For GC trends and alerting over time, prefer counter-based PromQL such as:\n  - `rate(monarch_jvm_gc_collection_count_total[5m])`\n  - `rate(monarch_jvm_gc_collection_time_seconds_total[5m])`\n\n## Prometheus And Grafana\n\nMonarchJavaAgent fits cleanly into a standard Prometheus and Grafana stack. Prometheus scrapes Monarch's `/metrics` endpoint, and Grafana visualizes the exported JVM signals without requiring a separate exporter.\n\nMinimal Prometheus scrape config:\n\n```yaml\nglobal:\n  scrape_interval: 10s\n\nscrape_configs:\n  - job_name: \"monarch-java-agent\"\n    static_configs:\n      - targets: [\"127.0.0.1:9090\"]\n```\n\nScrape interval guidance:\n\n- `10s` is a practical default because CPU metrics update every 10s.\n- Scraping faster than 10s is allowed, but usually returns repeated values for most series.\n- If your focus is only slow-moving JVM health metrics, `15s`-`30s` can reduce Prometheus load with minimal loss.\n\nCommon Prometheus queries:\n\n- `monarch_jvm_heap_used_bytes`\n- `monarch_jvm_cpu_process_percent`\n- `monarch_jvm_threads_current`\n- `monarch_jvm_classloader_loaded`\n\nExample Grafana dashboard using Monarch metrics:\n\n![Grafana dashboard for MonarchJavaAgent metrics](jekyll/source/grafana-dashboard.png.jpg)\n\n## Documentation\n\n- Jekyll source for the project site lives under `jekyll/source`.\n- GitHub Pages is built and deployed from workflow automation (not from committed `docs/` output).\n\n## Releasing\n\n- Release process runbook: [RELEASING.md](RELEASING.md)\n- Release workflow is tag-driven and validates tag version against `pom.xml`.\n\n## Building from Source\n\n```bash\ngit clone https://github.com/AshutoshIWNL/MonarchJavaAgent.git\ncd MonarchJavaAgent\nmvn clean package\n```\n\n## Smoke Testing (Startup + Runtime Attach)\n\nThe project now includes an integration smoke harness that validates:\n\n- `-javaagent` startup mode\n- Runtime attach mode via Attach API\n- Instrumentation markers for `ARGS`, `RET`, `STACK`, `PROFILE`, `ADD`, `CODEPOINT`, `HEAP`\n- Class replacement success/failure coverage for `CHANGE::FILE` and `CHANGE::JAR`\n- Metrics endpoint availability\n- Heap dump generation\n- Runtime config reload behavior (rule removal stops instrumentation effect without restart)\n- Invalid rule behavior (startup failure for malformed action rules)\n- Nested-vs-legacy config precedence behavior at runtime\n- Boundary safety behavior where startup/attach/transform failures are contained and do not crash the host JVM flow\n\n### Prerequisites\n\n- Java + Maven available in `PATH`\n- For runtime attach tests, set `JAVA_HOME` to a JDK (not JRE)\n- Profiling/instrumentation currently runs with bytecode verification disabled (`-Xverify:none`) in smoke scripts\n\n### Windows (PowerShell)\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\smoke-all.ps1\n```\n\nTo run individual suites:\n\n```powershell\n# full startup + attach coverage\npowershell -ExecutionPolicy Bypass -File scripts\\smoke-javaagent.ps1\npowershell -ExecutionPolicy Bypass -File scripts\\smoke-attach.ps1\n\n# quick partial checks (fast signal)\npowershell -ExecutionPolicy Bypass -File scripts\\smoke-quick.ps1\n\n# deep runtime config reload checks\npowershell -ExecutionPolicy Bypass -File scripts\\smoke-config-reload.ps1\n\n# invalid rule behavior check\npowershell -ExecutionPolicy Bypass -File scripts\\smoke-invalid-rule.ps1\n\n# nested config overrides legacy flat keys\npowershell -ExecutionPolicy Bypass -File scripts\\smoke-config-precedence.ps1\n\n# class replacement via redefine from class file and jar\npowershell -ExecutionPolicy Bypass -File scripts\\smoke-class-replace.ps1\n```\n\n### Linux/macOS (Shell)\n\n```bash\nchmod +x scripts/*.sh\nexport JAVA_HOME=/path/to/jdk\nbash scripts/smoke-all.sh\n```\n\nTo run individual modes:\n\n```bash\n# full startup + attach coverage\nbash scripts/smoke-javaagent.sh\nbash scripts/smoke-attach.sh\n\n# quick partial checks (fast signal)\nbash scripts/smoke-quick.sh\n\n# deep runtime config reload checks\nbash scripts/smoke-config-reload.sh\n\n# invalid rule behavior check\nbash scripts/smoke-invalid-rule.sh\n\n# nested config overrides legacy flat keys\nbash scripts/smoke-config-precedence.sh\n\n# class replacement via redefine from class file and jar\nbash scripts/smoke-class-replace.sh\n```\n\nSmoke run artifacts (trace/log/config) are written to temp run directories under:\n\n- Windows: `%TEMP%\\mja-smoke\\...`\n- Linux/macOS: `/tmp/mja-smoke/...`\n\n\n## Author\n\n- **Ashutosh Mishra** (https://github.com/AshutoshIWNL)\n\n## License\n\nThis project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fashutoshiwnl%2Fmonarchjavaagent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fashutoshiwnl%2Fmonarchjavaagent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fashutoshiwnl%2Fmonarchjavaagent/lists"}