A security log helps reconstruct who tried to do what, on which resource, when, and with what result. It is not a copy of every request or a dump of exceptions. Its purpose is to provide useful evidence for investigating suspicious access and actions, using the minimum necessary data and controls that prevent the log itself from becoming a source of exposure.
When designing security logging in PHP, first define the events that matter and how they will be used: detection, investigation, operational auditing, or compliance with an internal policy. Then establish a common format, decide who can access the records, and test the entire flow. A log that exists but does not let you order events, identify the outcome, or distinguish attempts from completed actions does not meet the need for investigation.
Separate technical logs, activity logs, and security events

Technical logs describe how the application works: errors, exceptions, latency, connection failures, or configuration problems. They help diagnose availability and behavior. Activity logs document business actions attributable to users, such as editing an invoice or changing account details. Security events, by contrast, focus on facts relevant to protecting identities, permissions, resources, and access boundaries.
These categories can overlap, but they are not interchangeable. A role change can be both a business activity and a security event; a database exception is usually technical, although a series of failures during a sensitive operation may warrant a security alert. Defining purposes avoids duplicating information without a clear rationale and makes it possible to assign appropriate retention periods, access controls, and formats.
Do not use the security log as an unlimited audit store or as a substitute for metrics and traces. Also, do not assume that recording an event is the same as detecting it: generating alerts requires rules, thresholds, and people responsible for responding. Document which system produces each data point and which team uses it.
Choose actions and context that support investigations
Start with an inventory of actions and threats relevant to the application. Events that often warrant consideration include failed logins, account lockouts, credential changes, permission grants or revocations, configuration changes, sensitive exports, and administrative operations. The list should reflect the product: recording every page view can create noise, while omitting a privilege change can make it impossible to reconstruct unauthorized access.
For each event, define the question it should help answer. When investigating a permission change, for example, it matters to identify the actor, the affected user or resource, the previous and new permissions, the outcome, and the time. For a failed authentication, a normalized or pseudonymized account identifier may be relevant, depending on the policy, along with the outcome and request origin if collecting it is justified. Avoid inferring identity from a shared IP address.
Distinguish between an attempt, a decision, and an outcome. A denied request is not a completed change; an accepted request can fail before being persisted. Record the actual outcome and, when needed for an investigation, the stage reached. Also define what each value means, such as denied, failed, or succeeded, so different components do not use them inconsistently.
Use a consistent, correlatable schema
A structured format, usually JSON, makes it easier to filter and correlate events in operational tools. As a starting point, consider these fields:
- Event identifier: a stable, versioned name, for example
authorization.permission_changed. - Timestamp: a UTC date and time with appropriate precision and synchronized system clocks.
- Actor: an internal identifier or pseudonym; explicitly represent processes and automated tasks.
- Action and resource: the operation performed and the type and identifier of the affected resource, without including its contents.
- Outcome: success, denial, or failure, with a categorized, non-sensitive reason.
- Correlation: a request or operation identifier that links events without dumping the entire request.
- Contextual origin: the service or component that emitted the event and, if justified, limited network-origin data.
Field names and types should remain stable. A request correlation identifier must be generated or validated by the infrastructure; accepting an arbitrary identifier without controls can cause collisions or confusion. Do not indiscriminately include full headers, HTTP bodies, or parameters. Context should help reconstruct the sequence, not replicate all the data processed.
Exclude secrets and minimize personal data
Never log passwords, access tokens, API keys, session secrets, recovery codes, or values that enable authentication. Also avoid complete personal data when an internal identifier is sufficient. For fields such as email addresses, phone numbers, or street addresses, decide whether storing them is essential, whether they can be masked, or whether a pseudonymized reference is more appropriate. The decision depends on the purpose, the risk, and applicable obligations.
Redaction should happen before the event leaves the process; do not rely solely on a downstream filter in the aggregator. In PHP, avoid directly serializing request objects, headers, or exceptions that may contain sensitive values. Create an explicit object or array with an allowlist of fields; a blocklist strategy is fragile when new fields appear. Also validate and limit the length of user-controlled values to reduce line injection, noise, and excessive consumption.
Test cases where secret data appears in parameters or error messages, and verify that it does not reach the log destination. Sanitization should preserve enough information to investigate, such as an error category, without retaining the full message if it could include credentials.
Protect access, integrity, and the lifecycle
Logs also contain sensitive information. Restrict who can write, read, export, and manage retention; these functions may require different permissions. Protect transport and storage with controls appropriate to the infrastructure, and prevent a compromised application account from silently deleting or altering the entire history. When justified by the risk, use append-only storage or verifiable integrity controls, and document their limitations.
Define explicit retention periods for each event class. Keeping logs indefinitely increases exposure and cost; deleting them too soon can hinder an investigation. The policy should account for backups, exports, test environments, and deletion requests that affect personal data. Record and review operators' access to the logs themselves, especially exports or policy changes.
Logging can fail because the destination is unavailable, storage limits are reached, or serialization errors occur. Decide what should happen in each case based on criticality. Do not let a logging system outage become a way to silently complete a high-risk operation, but do not let a logging exception cause inconsistent results or duplicate a transaction either. For critical operations, it may be necessary to ensure that the event is written together with the change using a transactional strategy, such as the outbox pattern. If a queue is used, define retries, deduplication, and how pending events are handled.
Test that the event exists and is safe
Tests should verify more than a call to a logging function. Exercise representative scenarios: access denied, privilege granted, privilege revoked, and a sensitive change completed or failed. Check that the event includes the actor, resource, outcome, timestamp, and correlation identifier; that the outcome matches what actually happened; and that no passwords, tokens, or unnecessary data appear.
Include tests for log destination failures and observe whether the main operation remains in a consistent state, whether an alternative signal is generated, and whether the error is visible to operations. For a sensitive action, confirm that success is not reported if the change was not persisted. Also test limits and manipulated input, such as newline characters in user-controlled values, as well as access by role: a business user should not have access to the event repository by default.
Implementation review checklist

- Does each event have a defined purpose, owner, and retention policy?
- Are attempts, denials, failures, and completed operations distinguished?
- Does the schema identify the actor, action, resource, outcome, and timestamp?
- Is the request correlated without recording its full contents?
- Are secrets excluded and personal data minimized before transmission?
- Are access, transport, storage, and deletion controlled?
- Have destination outages, operation integrity, and read permissions been tested?
First prioritize events that protect accounts, privileges, and the highest-impact operations. Then standardize the schema, limit the data, and automate tests that fail if a secret enters the logs. This way, security logging in PHP provides actionable evidence without becoming a second database of sensitive information.



