generator-jhipster-loggingmodule
v2.0.0
Published
JHipster module that logs every API request lifecycle (start, finish, error) to a dedicated rolling log file
Downloads
294
Maintainers
Readme
generator-jhipster-loggingmodule
A JHipster module that logs every API request lifecycle — start, finish, and error — to a dedicated rolling log file, completely separate from the main application log.
Table of Contents
- Overview
- Features
- Tech Stack
- Architecture
- Project Structure
- Getting Started
- Installation
- Configuration
- Usage
- Log Format
- Testing
- Deployment
- CI/CD
- Contributing
- Security
- License
- Contact
Overview
generator-jhipster-loggingmodule is a Yeoman/JHipster generator module that installs structured API request lifecycle logging into any existing JHipster Spring Boot application.
Once installed, it automatically:
- Intercepts every incoming HTTP request via a Spring
OncePerRequestFilter - Logs three distinct lifecycle events per request: START, FINISH, and ERROR
- Writes all logs to a dedicated rolling log file (separate from the main application log)
- Includes a unique request ID, authenticated user, timestamp, HTTP method, URI, and request/response body details in every log entry
Key goals of the project:
- Provide full observability of API request lifecycles without touching application code
- Produce structured, traceable logs that can be correlated across START/FINISH/ERROR events via a shared Request ID
- Keep the log file manageable with automatic weekly rotation and 30-day retention
Features
- Three-phase lifecycle logging — START (before handler), FINISH (200/201/204 responses), ERROR (non-2xx or unhandled exception)
- Unique Request ID — a UUID generated per request, shared across all three phases for easy tracing
- Authenticated user logging — captures the
jhi_user.loginfrom Spring Security context - Mandatory request body logging — always logged at START and on error
- Error response body logging — response body is always captured and logged for non-2xx responses
- Configurable HTTP method filtering — choose which methods (GET, POST, PUT, DELETE) to log
- Dedicated rolling log file — completely separate from the main application log
- Configurable log rotation — choose daily, weekly, or monthly rolling at install time; configurable via
application.ymlafterwards - Optional database logging — optionally persist every log entry to a MySQL
api_request_logtable; table is created automatically via Liquibase on first app start - Zero-code installation — a single
yo jhipster-loggingmodulecommand wires everything into your project - Idempotent generator — safe to re-run; will not duplicate configuration
- Configurable via
application.yml— no code changes needed to adjust settings
Tech Stack
Module (Generator):
- Node.js
- Yeoman (
yo) - generator-jhipster (BaseGenerator)
- EJS templating
Generated Components (Target App):
- Java / Spring Boot
- Spring Web (
OncePerRequestFilter,ContentCachingResponseWrapper) - Spring Security (
SecurityContextHolder) - Logback (
RollingFileAppender,TimeBasedRollingPolicy) - Jakarta Servlet API
Testing:
- Mocha
- yeoman-assert
- yeoman-test
Architecture
The module follows a generator → target app injection pattern:
generator-jhipster-loggingmodule (this repo)
│
│ yo jhipster-loggingmodule
▼
Your JHipster Spring Boot App
│
├── ApiLoggingFilter.java ← Spring filter (intercepts requests)
├── ApiLoggingProperties.java ← @ConfigurationProperties bean
├── logback-spring.xml ← API_FILE appender injected
└── application.yml ← api-logging: block appendedRuntime request flow:
Incoming HTTP Request
│
▼
CachedBodyRequestWrapper ← buffers request body for multi-read
ContentCachingResponseWrapper ← buffers response body for logging
│
▼
logStarted(...) ← >>> REQUEST STARTED (before handler)
│
▼
chain.doFilter(...) ← application handler runs here
│
├── status 200/201/204 → logFinished(...) ← <<< REQUEST FINISHED
├── status 4xx/5xx → logHttpError(...) ← !!! REQUEST ERROR
└── exception thrown → logError(...) ← !!! REQUEST ERROR
│
▼
wrappedResponse.copyBodyToResponse() ← sends response to clientConfiguration connection:
application.yml (api-logging.log-file)
│
├──► ApiLoggingProperties.java (Spring binds value into bean)
│ └── ApiLoggingFilter reads properties at runtime
│
└──► logback-spring.xml (<springProperty> reads same key)
└── API_FILE appender → logs/api-requests.logProject Structure
logging-module/
│
├── generators/
│ └── app/
│ ├── index.js # Main generator — all logic lives here
│ └── templates/
│ ├── ApiLoggingFilter.java # Spring filter template (EJS)
│ └── ApiLoggingProperties.java # Configuration properties template (EJS)
│
├── test/
│ └── app.spec.js # Mocha unit tests for the generator
│
├── package.json # Yeoman module definition & npm metadata
└── README.mdGetting Started
Requirements:
- Node.js 12+
- npm 6+
- Yeoman (
npm install -g yo) - An existing JHipster Spring Boot project (must have
.yo-rc.json) - Java 11+ / Spring Boot 3.x (Jakarta EE)
Installation
Install from npm (recommended):
npm install -g generator-jhipster-loggingmoduleOr install locally for development:
git clone https://your-repo/logging-module.git
cd logging-module
npm install
npm linkRun the generator inside your JHipster project:
cd /path/to/your-jhipster-app
yo jhipster-loggingmoduleThe generator will ask three questions:
- Enable API request logging? — default:
Yes - Log file path — default:
logs/api-requests.log - Which HTTP methods to log? — checkbox:
GET,POST,PUT,DELETE(all selected by default)
To skip all prompts and use defaults:
yo jhipster-loggingmodule defaultFiles generated / modified in the target app:
src/
├── main/
│ ├── java/<your-package>/
│ │ ├── config/
│ │ │ └── ApiLoggingProperties.java ← NEW
│ │ └── logging/
│ │ └── ApiLoggingFilter.java ← NEW
│ └── resources/
│ ├── logback-spring.xml ← MODIFIED (API_FILE appender injected)
│ └── config/
│ └── application.yml ← MODIFIED (api-logging block appended)Configuration
All settings live under the api-logging prefix in application.yml:
api-logging:
enabled: true # Set to false to disable all logging entirely
log-file: logs/api-requests.log # Path relative to JVM working directory (project root)
log-methods: # Only requests with these methods will be logged
- GET
- POST
- PUT
- DELETE
rolling-pattern: logs/api-requests.log.%d{yyyy-'W'ww} # Archive filename pattern — controls rolling frequency
max-history: 4 # Number of archives to keep before older ones are deletedConfiguration reference:
| YAML Key | Default | Description |
|-------------------------------|------------------------------------------------|-----------------------------------------------------------------|
| api-logging.enabled | true | Activates or deactivates the filter entirely |
| api-logging.log-file | logs/api-requests.log | Path of the active log file (relative to project root) |
| api-logging.log-methods | [GET, POST, PUT, DELETE] | HTTP methods to log; others are silently skipped |
| api-logging.rolling-pattern | logs/api-requests.log.%d{yyyy-'W'ww} | Archive filename pattern — controls rolling frequency |
| api-logging.max-history | 4 | Number of rolling archives to keep; older ones are auto-deleted |
| api-logging.log-to-database | false | Also persist log entries to the api_request_log database table|
Changes take effect on next application restart. No code changes or regeneration needed.
Adjusting the rolling frequency:
Change rolling-pattern to control how often a new archive file is created:
| rolling-pattern value | Rolling frequency | Recommended max-history |
|-------------------------------------------------|-------------------|---------------------------|
| logs/api-requests.log.%d{yyyy-MM-dd} | Daily | 30 (keeps 30 days) |
| logs/api-requests.log.%d{yyyy-'W'ww} | Weekly (default) | 4 (keeps ~30 days) |
| logs/api-requests.log.%d{yyyy-MM} | Monthly | 12 (keeps 1 year) |
The generator will prompt you to choose daily/weekly/monthly during installation and will automatically set the correct default for max-history. Both values can be freely changed in application.yml at any time after installation.
Database Logging (Optional)
When prompted during installation, you can choose to also save every log entry to the database. This is completely optional — file logging is always active regardless.
What gets generated when you say Yes:
src/
├── main/
│ ├── java/<your-package>/
│ │ ├── config/
│ │ │ └── LogApi.java ← NEW: @LogApi annotation
│ │ ├── domain/
│ │ │ └── ApiRequestLog.java ← NEW: JPA entity
│ │ └── repository/
│ │ └── ApiRequestLogRepository.java ← NEW: Spring Data repository
│ └── resources/config/liquibase/
│ ├── master.xml ← MODIFIED: include injected
│ └── changelog/
│ └── api_request_log.xml ← NEW: Liquibase changelogControlling which endpoints are logged to the database — @LogApi:
Database logging is opt-in per endpoint via the @LogApi annotation. File logging is always active for all matched methods regardless of this annotation.
Place @LogApi on a specific method to log only that endpoint:
import com.example.myapp.config.LogApi;
@RestController
@RequestMapping("/api")
public class UserResource {
@LogApi // ← only this method is saved to DB
@GetMapping("/users")
public List<User> getAllUsers() { ... }
@PostMapping("/users") // ← file-logged only, NOT saved to DB
public User createUser(@RequestBody User user) { ... }
}Place @LogApi on an entire controller to log all its methods:
@LogApi // ← every method in this class is saved to DB
@RestController
@RequestMapping("/api/payments")
public class PaymentResource {
@PostMapping("/charge")
public PaymentResult charge(@RequestBody ChargeRequest req) { ... }
@GetMapping("/history")
public List<Payment> getHistory() { ... }
}Note: Method-level
@LogApitakes precedence over class-level. You can annotate a whole controller with@LogApiand then the filter will match any method inside it.
Table structure (api_request_log):
| Column | Type | Description |
|-----------------|---------------|----------------------------------------------------------------------|
| id | BIGINT (PK) | Auto-increment primary key |
| request_id | VARCHAR(36) | UUID shared across all phases of the same request |
| phase | VARCHAR(10) | START, FINISH, or ERROR |
| log_date_time | DATETIME | Timestamp of the log entry |
| user_login | VARCHAR(50) | Authenticated user login, or anonymous |
| http_method | VARCHAR(10) | HTTP method (GET, POST, etc.) |
| uri | VARCHAR(500) | Request URI including query string |
| status | INTEGER | HTTP response status — null for START phase |
| duration_ms | BIGINT | Handler duration in ms — null for START phase |
| req_body | TEXT (CLOB) | Request body — present on START and exception ERROR phases |
| res_body | TEXT (CLOB) | Response body — present on HTTP error (non-2xx) ERROR phase only |
| error_type | VARCHAR(255) | Exception class name — present on exception ERROR phase only |
| error_message | TEXT (CLOB) | Exception message — present on exception ERROR phase only |
How to query your logs:
-- All events for a specific request (trace full lifecycle)
SELECT * FROM api_request_log WHERE request_id = 'f47ac10b-...' ORDER BY log_date_time;
-- All errors in the last 24 hours
SELECT * FROM api_request_log WHERE phase = 'ERROR' AND log_date_time >= NOW() - INTERVAL 1 DAY;
-- Slowest requests today
SELECT uri, http_method, duration_ms, user_login
FROM api_request_log
WHERE phase = 'FINISH' AND log_date_time >= CURDATE()
ORDER BY duration_ms DESC LIMIT 20;
-- Requests by a specific user
SELECT * FROM api_request_log WHERE user_login = 'john.doe' ORDER BY log_date_time DESC;Enabling/disabling at runtime:
Change api-logging.log-to-database in application.yml and restart — no code changes needed:
api-logging:
log-to-database: true # set to false to stop DB writes without removing the tableNote: If a database error occurs while saving a log entry, it is caught silently and logged as a warning in the file — the original HTTP request is never affected.
Usage
After installation, simply start your JHipster application normally:
./mvnw spring-boot:runThe filter activates automatically. Log entries will appear in logs/api-requests.log as soon as the first matching HTTP request is received.
Log Format
Each request produces up to two log entries — one at START and one at FINISH or ERROR.
START — fired before the handler runs:
>>> REQUEST STARTED
Date : 2025-03-10 14:32:01
Request-ID : f47ac10b-58cc-4372-a567-0e02b2c3d479
User : john.doe
Method : POST
URI : /api/users
ReqBody : {"login":"alice","email":"[email protected]"}FINISH — fired for 200/201/204 responses:
<<< REQUEST FINISHED
Date : 2025-03-10 14:32:01
Request-ID : f47ac10b-58cc-4372-a567-0e02b2c3d479
User : john.doe
Method : POST
URI : /api/users
Status : 201
Duration : 47 msERROR — fired for non-2xx HTTP responses or unhandled exceptions:
!!! REQUEST ERROR
Date : 2025-03-10 14:32:05
Request-ID : 9b2e1c3a-1234-4abc-8def-000011112222
User : jane.doe
Method : DELETE
URI : /api/users/999
Status : 404
Duration : 12 ms
ResBody : {"title":"Not Found","status":404,"detail":"User 999 not found"}The Request-ID is identical across START and FINISH/ERROR entries for the same request, allowing you to correlate them in the log file.
Testing
Run the generator unit tests:
npm testTest coverage:
| Test | What it verifies |
|------|-----------------|
| generates ApiLoggingFilter | File exists at the correct path |
| generates ApiLoggingProperties | File exists at the correct path |
| ApiLoggingFilter has correct package | EJS substitution worked correctly |
| ApiLoggingProperties uses @ConfigurationProperties | Annotation is present |
| logback-spring.xml has API_FILE appender | Appender was injected |
| logback-spring.xml has API_REQUEST_LOG logger | Logger was injected |
| logback-spring.xml reads log path from Spring property | <springProperty> with correct source attribute |
Deployment
Publishing to npm:
Ensure you are logged in:
npm whoami
npm login # if not logged inPublish the package:
npm publishNote: npm requires 2FA or a Granular Access Token to publish packages.
To generate a token: npm website → Account Settings → Access Tokens → Generate New Token → Granular Access Token → Read and Write on this package.
Then publish using the token:
npm publish --_authToken=<your-token>Verify the published package:
npm view generator-jhipster-loggingmoduleCI/CD
CI/CD is configured using GitHub Actions (.github/workflows/github-ci.yml).
Pipeline steps:
- Install Node.js dependencies (
npm install) - Run unit tests (
npm test) - Build / lint validation
- Publish to npm on release tag (with access token from GitHub Secrets)
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/your-feature) - Make your changes and add/update tests in
test/app.spec.js - Ensure all tests pass (
npm test) - Commit your changes (
git commit -m "feat: add your feature") - Open a Pull Request against
main
Security
- The filter reads the authenticated user from Spring Security context (
SecurityContextHolder) — it does not store or transmit credentials - Request bodies are logged as-is; avoid logging endpoints that handle raw passwords or sensitive tokens
- The log file is written to the local filesystem; ensure appropriate file permissions are set on the
logs/directory in production - To disable logging entirely, set
api-logging.enabled: falseinapplication.yml— no redeployment required, only a restart
License
MIT License
Contact
Maintainer: mariam.ibrahim
