diff --git a/Marco.Pms.Services/Controllers/LogController.cs b/Marco.Pms.Services/Controllers/LogController.cs
new file mode 100644
index 0000000..6047642
--- /dev/null
+++ b/Marco.Pms.Services/Controllers/LogController.cs
@@ -0,0 +1,130 @@
+using System.Text.Json;
+using Marco.Pms.Model.Logs;
+using MarcoBMS.Services.Service;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Serilog.Context;
+
+namespace Marco.Pms.Services.Controllers
+{
+ [Authorize]
+ [Route("api/[controller]")]
+ [ApiController]
+ public class LogController : ControllerBase
+ {
+ private readonly ILoggingService _logger;
+ public LogController(ILoggingService logger)
+ {
+ _logger = logger;
+ }
+ [HttpPost]
+ ///
+ /// Receives a batch of log entries from mobile applications and processes them.
+ /// Logs are enriched with context properties like UserAgent, Timestamp, and IpAddress.
+ ///
+ /// A list of LogStructure objects containing log details.
+ /// An indicating the success of the operation.
+ public IActionResult MobileLogging([FromBody] List logs)
+ {
+ var demoDetails = new
+ {
+ name = "Pooja",
+ job = "Tester",
+ org = "MarcoAiot"
+ };
+ // Check if logs are provided to avoid processing empty requests
+ if (logs == null || !logs.Any())
+ {
+ _logger.LogWarning("MobileLogging endpoint received an empty or null log list.");
+ return BadRequest("No log entries provided.");
+ }
+
+ // Iterate through each log entry received from the client
+ foreach (var log in logs)
+ {
+ object? detailsObject = null;
+
+ // Attempt to deserialize the 'Details' string into a dynamic object
+ // This allows structured logging of the details payload.
+ log.Details = JsonSerializer.Serialize(demoDetails);
+ if (!string.IsNullOrWhiteSpace(log.Details))
+ {
+ try
+ {
+ // Use JsonDocument for potentially more efficient parsing if you just need the raw JSON or a dynamic object
+ // For simply logging it as a structured property, object works.
+ detailsObject = JsonSerializer.Deserialize>(log.Details);
+
+ }
+ catch (JsonException ex)
+ {
+ // Log a warning if deserialization fails, but continue processing other logs
+ _logger.LogWarning("Failed to deserialize 'Details' for a log entry. Raw details: '{RawDetails}'. Error: {ErrorMessage}", log.Details, ex.Message);
+ }
+ }
+
+ // Push common properties to the Serilog LogContext for the current log operation.
+ // These properties will be attached to the log event itself.
+ using (LogContext.PushProperty("UserAgent", log.UserAgent))
+ using (LogContext.PushProperty("Timestamp", log.TimeStamp))
+ using (LogContext.PushProperty("IpAddress", log.IpAddress))
+ {
+ // If details were successfully parsed, push them as a structured property.
+ if (detailsObject != null)
+ {
+ using (LogContext.PushProperty("Details", detailsObject, true)) // 'true' for destructuring
+ {
+ LogByLevel(log.LogLevel, log.Message);
+ }
+ }
+ else
+ {
+ // If details were null or failed to parse, log without the details property
+ LogByLevel(log.LogLevel, log.Message);
+ }
+ }
+ }
+
+ // Return 200 OK indicating successful receipt of logs.
+ // As logging is typically a "fire and forget" operation from the client's perspective,
+ // we don't need to await individual log writes here.
+ // The logger provider handles the actual writing asynchronously in the background.
+ _logger.LogInfo("Successfully processed {LogCount} mobile log entries.", logs.Count);
+ return Ok("Logs received successfully.");
+ }
+
+ ///
+ /// Logs a message at the specified log level using the injected ILogger.
+ ///
+ /// The string representation of the log level (e.g., "info", "warning").
+ /// The log message.
+ private void LogByLevel(string? logLevel, string? message)
+ {
+ // Default message if null or empty
+ message = string.IsNullOrWhiteSpace(message) ? "No message provided." : message;
+
+ // Use a switch expression or simple if/else for clarity based on log level
+ switch (logLevel?.ToLowerInvariant()) // Use ToLowerInvariant for case-insensitive comparison
+ {
+ case "info":
+ case "information": // Common alias for info
+ _logger.LogInfo(message);
+ break;
+ case "warn":
+ case "warning":
+ _logger.LogWarning(message);
+ break;
+ case "error":
+ _logger.LogError(message);
+ break;
+ case "critical":
+ _logger.LogCritical(message);
+ break;
+ default:
+ // Log unknown levels as information to ensure they are captured
+ _logger.LogInfo("[Unknown Level] {Message}", message);
+ break;
+ }
+ }
+ }
+}
diff --git a/Marco.Pms.Services/Service/ILoggingService.cs b/Marco.Pms.Services/Service/ILoggingService.cs
index 39dbb00..bf97e35 100644
--- a/Marco.Pms.Services/Service/ILoggingService.cs
+++ b/Marco.Pms.Services/Service/ILoggingService.cs
@@ -1,12 +1,11 @@
-using Serilog.Context;
-
-namespace MarcoBMS.Services.Service
+namespace MarcoBMS.Services.Service
{
public interface ILoggingService
{
void LogInfo(string? message, params object[]? args);
void LogWarning(string? message, params object[]? args);
void LogError(string? message, params object[]? args);
+ void LogCritical(string? message, params object[]? args);
}
}
diff --git a/Marco.Pms.Services/Service/LoggingServices.cs b/Marco.Pms.Services/Service/LoggingServices.cs
index 4328a2a..c6a7577 100644
--- a/Marco.Pms.Services/Service/LoggingServices.cs
+++ b/Marco.Pms.Services/Service/LoggingServices.cs
@@ -18,10 +18,11 @@ namespace MarcoBMS.Services.Service
{
_logger.LogError(message, args);
}
- else {
+ else
+ {
_logger.LogError(message);
}
- }
+ }
public void LogInfo(string? message, params object[]? args)
{
@@ -48,6 +49,19 @@ namespace MarcoBMS.Services.Service
_logger.LogWarning(message);
}
}
+
+ public void LogCritical(string? message, params object[]? args)
+ {
+ using (LogContext.PushProperty("LogLevel", "Critical"))
+ if (args != null)
+ {
+ _logger.LogCritical(message, args);
+ }
+ else
+ {
+ _logger.LogCritical(message);
+ }
+ }
}
}