Pharos 0.7.23
Modern Web Framework & Web App Hosting Service.
Loading...
Searching...
No Matches
native_http_access_log.c
Go to the documentation of this file.
1/**
2 * @file
3 * @brief Implements native access log and error log helpers.
4 */
5
7
9#include "native_fs.h"
10#include "native_http_log.h"
11#include "native_support.h"
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <time.h>
17
18#ifdef _WIN32
19#include <sys/time.h>
20#define native_http_access_log_strdup _strdup
21#else
22#include <sys/time.h>
23#define native_http_access_log_strdup strdup
24#endif
25
26static int close_logged_file_native(FILE *file) {
27 if (file == NULL) {
28 return 0;
29 }
30 if (ferror(file)) {
31 fclose(file);
32 return -1;
33 }
34 return fclose(file) == 0 ? 0 : -1;
35}
36
37static char *access_timestamp_native(void) {
38 struct timeval tv;
39 struct tm tm_utc;
40 char buffer[32];
41 gettimeofday(&tv, NULL);
42#ifdef _WIN32
43 gmtime_s(&tm_utc, (const time_t *)&tv.tv_sec);
44#else
45 gmtime_r(&tv.tv_sec, &tm_utc);
46#endif
47 strftime(buffer, sizeof(buffer), "%Y%m%d.%H%M%S.", &tm_utc);
48 snprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer), "%03ld", (long)(tv.tv_usec / 1000));
49 return native_http_access_log_strdup(buffer);
50}
51
52static char *resolved_error_report_path_native(const char *app_id, const char *error_report_path) {
53 if (error_report_path != NULL && error_report_path[0] != '\0') {
54 return native_http_access_log_strdup(error_report_path);
55 }
56 {
57 size_t needed = strlen("/var/log/pharos/.error.jsonl") + strlen(app_id != NULL && app_id[0] != '\0' ? app_id : "app") + 1;
58 char *resolved = (char *)malloc(needed);
59 if (resolved != NULL) {
60 snprintf(resolved, needed, "/var/log/pharos/%s.error.jsonl", app_id != NULL && app_id[0] != '\0' ? app_id : "app");
61 }
62 return resolved;
63 }
64}
65
66static void write_error_report_native(const char *app_id, const char *error_report_path, const HttpRequest *request, const char *request_path, const char *status_text, const char *reason, const char *resource_path, const char *detail_text) {
67 char *resolved_path = NULL;
68 char *timestamp = NULL;
69 char *real_ip = NULL;
70 char *app_id_json = NULL;
71 char *timestamp_json = NULL;
72 char *status_json = NULL;
73 char *reason_json = NULL;
74 char *method_json = NULL;
75 char *path_json = NULL;
76 char *client_ip_json = NULL;
77 char *resource_path_json = NULL;
78 char *detail_json = NULL;
79 FILE *file = NULL;
80 char *dir = NULL;
81 if (request == NULL) {
82 return;
83 }
84 resolved_path = resolved_error_report_path_native(app_id, error_report_path);
85 if (resolved_path == NULL) {
86 return;
87 }
88 dir = path_dirname(resolved_path);
89 if (dir == NULL || ensure_directory(dir) != 0) {
90 free(dir);
91 free(resolved_path);
92 {
93 const char *fallback_app = app_id != NULL && app_id[0] != '\0' ? app_id : "app";
94 size_t needed = strlen("/tmp/pharos/.error.jsonl") + strlen(fallback_app) + 1;
95 resolved_path = (char *)malloc(needed);
96 if (resolved_path == NULL) {
97 return;
98 }
99 snprintf(resolved_path, needed, "/tmp/pharos/%s.error.jsonl", fallback_app);
100 dir = path_dirname(resolved_path);
101 if (dir != NULL) {
102 ensure_directory(dir);
103 }
104 }
105 }
106 free(dir);
107 file = fopen(resolved_path, "ab");
108 free(resolved_path);
109 if (file == NULL) {
110 return;
111 }
112 timestamp = access_timestamp_native();
113 real_ip = request_real_ip_native(request->headers);
114 app_id_json = json_string_dup_native(app_id != NULL && app_id[0] != '\0' ? app_id : "app");
115 timestamp_json = json_string_dup_native(timestamp != NULL ? timestamp : "");
116 status_json = json_string_dup_native(status_text != NULL ? status_text : "500 Internal Server Error");
117 reason_json = json_string_dup_native(reason != NULL ? reason : "http_response");
118 method_json = json_string_dup_native(request->method != NULL ? request->method : "");
119 path_json = json_string_dup_native(request_path != NULL ? request_path : "");
120 client_ip_json = json_string_dup_native(real_ip != NULL ? real_ip : "unknown");
121 resource_path_json = (resource_path != NULL && resource_path[0] != '\0') ? json_string_dup_native(resource_path) : native_http_access_log_strdup("null");
122 detail_json = (detail_text != NULL && detail_text[0] != '\0') ? json_string_dup_native(detail_text) : native_http_access_log_strdup("null");
123 if (app_id_json != NULL && timestamp_json != NULL && status_json != NULL && reason_json != NULL && method_json != NULL && path_json != NULL && client_ip_json != NULL && resource_path_json != NULL && detail_json != NULL) {
124 if (fprintf(file, "{\"timestamp\":%s,\"app_id\":%s,\"status\":%s,\"reason\":%s,\"method\":%s,\"path\":%s,\"client_ip\":%s,\"resource_path\":%s,\"detail\":%s}\n", timestamp_json, app_id_json, status_json, reason_json, method_json, path_json, client_ip_json, resource_path_json, detail_json) < 0) {
125 clearerr(file);
126 }
127 }
128 free(timestamp);
129 free(real_ip);
130 free(app_id_json);
131 free(timestamp_json);
132 free(status_json);
133 free(reason_json);
134 free(method_json);
135 free(path_json);
136 free(client_ip_json);
137 free(resource_path_json);
138 free(detail_json);
140}
141
142void native_write_error_log(const char *log_path, const char *error_report_path, const char *app_id, const HttpRequest *request, const char *request_path, const char *status_text, const char *reason, const char *resource_path, const char *detail_text) {
143 FILE *file;
144 char *timestamp = NULL;
145 char *real_ip = NULL;
146 char *headers_with_ip = NULL;
147 char *headers_compact = NULL;
148 char *dir = NULL;
149 if (log_path == NULL || log_path[0] == '\0' || request == NULL) {
150 return;
151 }
152 dir = path_dirname(log_path);
153 if (dir != NULL) {
154 ensure_directory(dir);
155 }
156 free(dir);
157 file = fopen(log_path, "ab");
158 if (file == NULL) {
159 return;
160 }
161 timestamp = access_timestamp_native();
162 real_ip = request_real_ip_native(request->headers);
163 headers_with_ip = request_headers_with_client_ip_native(request->headers, real_ip);
164 headers_compact = compact_log_text_native(headers_with_ip != NULL ? headers_with_ip : request->headers);
165 if (timestamp != NULL && real_ip != NULL && headers_compact != NULL) {
166 fprintf(
167 file,
168 "%s error app=%s status: \"%s\" method: \"%s\" path: \"%s\" headers: \"%s\" reason: \"%s\"",
169 timestamp,
170 app_id != NULL && app_id[0] != '\0' ? app_id : "app",
171 status_text != NULL ? status_text : "500 Internal Server Error",
172 request->method != NULL ? request->method : "",
173 request_path != NULL ? request_path : "",
174 headers_compact,
175 reason != NULL ? reason : "http_response"
176 );
177 if (resource_path != NULL && resource_path[0] != '\0') {
178 fprintf(file, " resource: \"%s\"", resource_path);
179 }
180 if (detail_text != NULL && detail_text[0] != '\0') {
181 char *detail_compact = compact_log_text_native(detail_text);
182 if (detail_compact != NULL) {
183 fprintf(file, " detail: \"%s\"", detail_compact);
184 }
185 free(detail_compact);
186 }
187 fputc('\n', file);
188 }
189 free(timestamp);
190 free(real_ip);
191 free(headers_with_ip);
192 free(headers_compact);
194 write_error_report_native(app_id, error_report_path, request, request_path, status_text, reason, resource_path, detail_text);
195}
196
197void native_write_access_log(const char *log_path, const HttpRequest *request, const char *request_path, int status_code) {
198 FILE *file;
199 char *timestamp = NULL;
200 char *real_ip = NULL;
201 char *headers_with_ip = NULL;
202 char *headers_compact = NULL;
203 char *dir = NULL;
204 if (log_path == NULL || log_path[0] == '\0' || request == NULL) {
205 return;
206 }
207 dir = path_dirname(log_path);
208 if (dir != NULL) {
209 ensure_directory(dir);
210 }
211 free(dir);
212 file = fopen(log_path, "ab");
213 if (file == NULL) {
214 return;
215 }
216 timestamp = access_timestamp_native();
217 real_ip = request_real_ip_native(request->headers);
218 headers_with_ip = request_headers_with_client_ip_native(request->headers, real_ip);
219 headers_compact = compact_log_text_native(headers_with_ip != NULL ? headers_with_ip : request->headers);
220 if (timestamp != NULL && real_ip != NULL && headers_compact != NULL) {
221 fprintf(
222 file,
223 "%s access status: \"%s\" method: \"%s\" path: \"%s\" headers: \"%s\"\n",
224 timestamp,
225 http_status_text_native(status_code),
226 request->method != NULL ? request->method : "",
227 request_path != NULL ? request_path : "",
228 headers_compact
229 );
230 }
231 free(timestamp);
232 free(real_ip);
233 free(headers_with_ip);
234 free(headers_compact);
236}
char * json_string_dup_native(const char *value)
Duplicates a string while escaping it for JSON string output.
Declares helpers that build native artifact and report payload strings.
int ensure_directory(const char *path)
Creates a directory path and any missing parent directories.
Definition native_fs.c:31
Declares native filesystem helper routines.
static void write_error_report_native(const char *app_id, const char *error_report_path, const HttpRequest *request, const char *request_path, const char *status_text, const char *reason, const char *resource_path, const char *detail_text)
static char * access_timestamp_native(void)
static char * resolved_error_report_path_native(const char *app_id, const char *error_report_path)
void native_write_access_log(const char *log_path, const HttpRequest *request, const char *request_path, int status_code)
Writes one access log entry for a completed HTTP request.
#define native_http_access_log_strdup
static int close_logged_file_native(FILE *file)
void native_write_error_log(const char *log_path, const char *error_report_path, const char *app_id, const HttpRequest *request, const char *request_path, const char *status_text, const char *reason, const char *resource_path, const char *detail_text)
Writes one structured error log entry and its paired JSONL error report.
Declares native access log and error log helpers.
char * compact_log_text_native(const char *value)
Compacts free-form text into a single-line log-safe representation.
char * request_real_ip_native(const char *headers)
Extracts the best client IP address from request headers.
char * request_headers_with_client_ip_native(const char *headers, const char *client_ip)
Returns request headers with an explicit client IP header applied.
const char * http_status_text_native(int status_code)
Returns the canonical reason phrase for one HTTP status code.
Declares HTTP logging and response formatting helpers.
char * path_dirname(const char *path)
Returns the parent directory component of a path.
Declares shared low-level support helpers used across the Pharos native runtime.
Parsed HTTP request fields extracted from a native connection.