Skip to content

Commit d583bed

Browse files
committed
Logging performance extreme boost and redesigned log structure
1 parent ea0f1fc commit d583bed

File tree

12 files changed

+763
-246
lines changed

12 files changed

+763
-246
lines changed
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
using System.Text;
2+
using FluentMinimalApiMapper;
3+
using Microsoft.AspNetCore.Mvc;
4+
5+
namespace SharedKernel.Demo;
6+
7+
public class LoggingTestEndpoints : IEndpoint
8+
{
9+
public void AddRoutes(IEndpointRouteBuilder app)
10+
{
11+
var grp = app.MapGroup("/tests")
12+
.WithTags("logs");
13+
14+
grp.MapPost("/json", ([FromBody] TestTypes payload) => Results.Ok(payload));
15+
16+
grp.MapPost("/json-array",
17+
([FromBody] int[] numbers) => Results.Ok(new
18+
{
19+
count = numbers?.Length ?? 0,
20+
numbers
21+
}));
22+
23+
grp.MapPost("/form-urlencoded", ([FromForm] FormUrlDto form) => Results.Ok(form))
24+
.DisableAntiforgery();
25+
26+
grp.MapGet("/json-per-property",
27+
() =>
28+
{
29+
var big = new string('x', 6_000); // ~6KB
30+
var payload = new
31+
{
32+
small = "ok",
33+
bigString = big, // should be redacted/omitted per-property
34+
tail = "done"
35+
};
36+
return Results.Json(payload);
37+
});
38+
39+
40+
grp.MapPost("/multipart",
41+
async ([FromForm] MultipartDto form) =>
42+
{
43+
var meta = new
44+
{
45+
form.Description,
46+
File = form.File is null
47+
? null
48+
: new
49+
{
50+
form.File.FileName,
51+
form.File.ContentType,
52+
form.File.Length
53+
}
54+
};
55+
return Results.Ok(meta);
56+
})
57+
.DisableAntiforgery();
58+
59+
grp.MapGet("/query", ([AsParameters] QueryDto q) => Results.Ok(q));
60+
61+
grp.MapGet("/route/{id:int}",
62+
(int id) => Results.Ok(new
63+
{
64+
id
65+
}));
66+
67+
grp.MapPost("/headers",
68+
([FromHeader(Name = "x-trace-id")] string? traceId, HttpRequest req) =>
69+
{
70+
var hasAuth = req.Headers.ContainsKey("Authorization");
71+
return Results.Ok(new
72+
{
73+
traceId,
74+
hasAuth
75+
});
76+
});
77+
78+
grp.MapGet("/binary",
79+
() =>
80+
{
81+
var bytes = Encoding.UTF8.GetBytes("Hello Binary");
82+
return Results.File(bytes, "application/octet-stream", "demo.bin");
83+
});
84+
85+
grp.MapGet("/no-content-type",
86+
async (HttpContext ctx) =>
87+
{
88+
await ctx.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("raw body with no content-type"));
89+
});
90+
91+
grp.MapGet("/large-json",
92+
() =>
93+
{
94+
var items = Enumerable.Range(1, 10_000)
95+
.Select(i => new
96+
{
97+
i,
98+
text = "xxxxxxxxxx"
99+
});
100+
return Results.Ok(items);
101+
});
102+
103+
grp.MapGet("/large-text",
104+
() =>
105+
{
106+
var sb = new StringBuilder();
107+
for (var i = 0; i < 20_000; i++) sb.Append('x');
108+
return Results.Text(sb.ToString(), "text/plain", Encoding.UTF8);
109+
});
110+
111+
grp.MapPost("/echo-with-headers",
112+
([FromBody] SharedKernel.Demo.TestTypes payload, HttpResponse res) =>
113+
{
114+
res.Headers["Custom-Header-Response"] = "CustomValue";
115+
res.ContentType = "application/json; charset=utf-8";
116+
return Results.Json(payload);
117+
});
118+
119+
grp.MapGet("/ping", () => Results.Text("pong", "text/plain"));
120+
121+
122+
grp.MapGet("/invalid-json",
123+
async (HttpContext ctx) =>
124+
{
125+
ctx.Response.StatusCode = StatusCodes.Status200OK;
126+
ctx.Response.ContentType = "application/json";
127+
await ctx.Response.Body.WriteAsync("{ invalid-json: true"u8.ToArray());
128+
});
129+
130+
// Your HttpClient test moved here, targets our echo endpoint
131+
grp.MapGet("/httpclient",
132+
async (IHttpClientFactory httpClientFactory) =>
133+
{
134+
var httpClient = httpClientFactory.CreateClient("RandomApiClient");
135+
httpClient.DefaultRequestHeaders.Add("auth", "hardcoded-auth-value");
136+
137+
var body = new SharedKernel.Demo.TestTypes
138+
{
139+
AnimalType = AnimalType.Cat,
140+
JustText = "Hello from Get Data",
141+
JustNumber = 100
142+
};
143+
144+
var content = new StringContent(System.Text.Json.JsonSerializer.Serialize(body),
145+
System.Text.Encoding.UTF8,
146+
"application/json");
147+
148+
var response = await httpClient.PostAsync("tests/echo-with-headers?barev=5", content);
149+
150+
if (!response.IsSuccessStatusCode)
151+
throw new Exception("Something went wrong");
152+
153+
var responseBody = await response.Content.ReadAsStringAsync();
154+
var testTypes = System.Text.Json.JsonSerializer.Deserialize<SharedKernel.Demo.TestTypes>(responseBody);
155+
156+
if (testTypes == null)
157+
throw new Exception("Failed to get data from external API");
158+
159+
return TypedResults.Ok(testTypes);
160+
});
161+
}
162+
}
163+
164+
public record FormUrlDto(string? Username, string? Password, string? Note);
165+
166+
public class MultipartDto
167+
{
168+
public IFormFile? File { get; init; }
169+
public string? Description { get; init; }
170+
}
171+
172+
public record QueryDto(int Page = 1, int PageSize = 10, string? Search = null, string[]? Tags = null);

SharedKernel.Demo/Program.cs

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,6 @@
8989
});
9090
});
9191

92-
app.MapPost("user",
93-
async ([FromBody] UserCommand user, ISender sender) =>
94-
{
95-
await sender.Send(user);
96-
return Results.Ok();
97-
});
98-
9992
app.MapPost("/receive-file", ([FromForm] IFormFile file) => TypedResults.Ok())
10093
.DisableAntiforgery();
10194

@@ -110,41 +103,6 @@
110103
return TypedResults.Ok(testTypes);
111104
});
112105

113-
app.MapGet("/get-data",
114-
async (IHttpClientFactory httpClientFactory) =>
115-
{
116-
var httpClient = httpClientFactory.CreateClient("RandomApiClient");
117-
httpClient.DefaultRequestHeaders.Add("auth", "hardcoded-auth-value");
118-
119-
var body = new TestTypes
120-
{
121-
AnimalType = AnimalType.Cat,
122-
JustText = "Hello from Get Data",
123-
JustNumber = 100
124-
};
125-
var content = new StringContent(System.Text.Json.JsonSerializer.Serialize(body),
126-
System.Text.Encoding.UTF8,
127-
"application/json");
128-
129-
var response = await httpClient.PostAsync("body?barev=5", content);
130-
131-
if (!response.IsSuccessStatusCode)
132-
{
133-
throw new Exception("Something went wrong");
134-
}
135-
136-
var responseBody = await response.Content.ReadAsStringAsync();
137-
138-
var testTypes = System.Text.Json.JsonSerializer.Deserialize<TestTypes>(responseBody);
139-
140-
if (testTypes == null)
141-
{
142-
throw new Exception("Failed to get data from external API");
143-
}
144-
145-
return TypedResults.Ok(testTypes);
146-
});
147-
148106
app.MapHub<MessageHub>("/hub");
149107

150108
app.LogStartSuccess();
@@ -165,38 +123,4 @@ public enum AnimalType
165123
Cat,
166124
Fish
167125
}
168-
169-
public record UserCommand(string Name, string Email) : ICommand<string>;
170-
171-
public class UserCommandHandler : ICommandHandler<UserCommand, string>
172-
{
173-
public Task<string> Handle(UserCommand request, CancellationToken cancellationToken)
174-
{
175-
return Task.FromResult($"User {request.Name} with email {request.Email} created successfully.");
176-
}
177-
}
178-
179-
public class User
180-
{
181-
public string Name { get; set; } = string.Empty;
182-
public string Email { get; set; } = string.Empty;
183-
}
184-
185-
public class UserValidator : AbstractValidator<UserCommand>
186-
{
187-
public UserValidator()
188-
{
189-
RuleFor(x => x.Name)
190-
.NotEmpty()
191-
.WithMessage("Name is required.")
192-
.MaximumLength(100)
193-
.WithMessage("Name cannot exceed 100 characters.");
194-
195-
RuleFor(x => x.Email)
196-
.NotEmpty()
197-
.WithMessage("Email is required.")
198-
.EmailAddress()
199-
.WithMessage("Invalid email format.");
200-
}
201-
}
202126
}

0 commit comments

Comments
 (0)