Document_Manager #129

Merged
ashutosh.nehete merged 83 commits from Document_Manager into main 2025-09-11 04:12:01 +00:00
3 changed files with 148 additions and 5 deletions
Showing only changes of commit bb76047605 - Show all commits

View File

@ -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]
/// <summary>
/// Receives a batch of log entries from mobile applications and processes them.
/// Logs are enriched with context properties like UserAgent, Timestamp, and IpAddress.
/// </summary>
/// <param name="logs">A list of LogStructure objects containing log details.</param>
/// <returns>An <see cref="IActionResult"/> indicating the success of the operation.</returns>
public IActionResult MobileLogging([FromBody] List<LogStructure> 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<Dictionary<string, string>>(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.");
}
/// <summary>
/// Logs a message at the specified log level using the injected ILogger.
/// </summary>
/// <param name="logLevel">The string representation of the log level (e.g., "info", "warning").</param>
/// <param name="message">The log message.</param>
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;
}
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}
}