neuron-explorer
Qwen3.5‑4B 전용 조향형 뉴런 탐색기, 모든 MLP 뉴런을 밀어 결과를 보관·탐색
ASP.NET Core 로그·메트릭·트레이스·알림을 앱에 직접 삽입, 실제 수집·외부 의존 없음, Program.cs 두 호출
Logs, metrics, traces, and alerts for ASP.NET Core, embedded in your own app with real ingestion and zero external dependencies. Two calls in Program.cs.
개발 도구C# · VueGitHub @Aban-Inc온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기An embedded, self-hosted observability dashboard for ASP.NET Core: logs, metrics, traces, and alerting, captured automatically from what your app already produces and served from inside your own process.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
An embedded, self-hosted observability dashboard for ASP.NET Core: logs,
metrics, traces, and alerting, captured automatically from what your app
already produces and served from inside your own process.
No collector, no message broker, no external database, no Redis, no
separate process to run or deploy. One NuGet package, two lines in
Program.cs.README 전문 보기 ↗An embedded, self-hosted observability dashboard for ASP.NET Core: logs, metrics, traces, and alerting, captured automatically from what your app already produces and served from inside your own process.
builder.Services.AddNafasServer(); // storage + real ingestion + alert evaluation
app.UseNafasDashboard("/nafas"); // mounts the dashboard UI at /nafas
No collector, no message broker, no external database, no Redis, no
separate process to run or deploy. One NuGet package, two lines in
Program.cs.
Standing up observability for a small or medium ASP.NET Core service usually means an OTel Collector, a time-series database, and a separate dashboard to run and secure: real infrastructure for a problem that's often just "let me see recent errors and know when something breaks." Nafas is the alternative for that case: it lives inside your application's own process, stores data in a local SQLite file (or your existing SQL Server) by default, and needs nothing external to run.
It is not a replacement for a full OTel/Prometheus/Grafana stack at scale. It does not federate across services, it does not do long-term high-cardinality storage, and it does not export to other backends. It is for the case where a single self-hosted app (or a handful of them sharing a database) needs real logs, metrics, traces, and threshold-based alerting without introducing new infrastructure to operate.
dotnet add package Nafas.Observability
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddNafasServer(options =>
{
// Every option below is optional -- these are the actual defaults.
options.Provider = DatabaseProvider.Sqlite;
options.RetentionDays = 30;
// options.AlertWebhookUrl = "https://example.com/webhooks/nafas";
});
var app = builder.Build();
app.UseNafasDashboard("/nafas"); // -> https://your-app/nafas
app.Run();
That's it: no instrumentation code to add anywhere else. Every
ILogger call, every Activity/span (including ASP.NET Core's own
per-request span), and every Meter measurement (including ASP.NET Core
8+'s built-in request-duration metric) your app already produces is
captured from this point on and shown on a real-time dashboard mounted
directly inside your own app, in English or Persian, light or dark.
Nafas hooks into the same extension points ASP.NET Core and
Microsoft.Extensions.Logging/System.Diagnostics already expose. It
does not require replacing ILogger<T>, wrapping HttpClient, or adding
any [Trace]-style attributes to your code:
| Signal | Source | Notes |
|---|---|---|
| Logs | An ILoggerProvider registered alongside your existing providers | Every ILogger.Log* call in the app, correlated to the active Activity when there is one |
| Traces | A process-wide System.Diagnostics.ActivityListener | Listens to every ActivitySource, including ASP.NET Core's own built-in per-request activity; no manual span creation needed for basic request tracing |
| Metrics | A process-wide System.Diagnostics.Metrics.MeterListener | Listens to every Meter, including ASP.NET Core 8+'s built-in http.server.request.duration, from which request-rate is also derived |
| Resource usage | A native sampler (Process + GC.GetGCMemoryInfo(), with a Windows-API fallback; see Requirements) | CPU and memory usage of the running process, sampled on an interval |
All of it is written through a bounded, non-blocking queue
(System.Threading.Channels) so a burst of telemetry never applies
backpressure to your request pipeline. Under sustained overload, Nafas
drops its own data rather than slow your app down. A dedicated background
service drains and batches that queue into storage, and the dashboard's
live views (the log stream, in particular) are pushed to the browser over
Server-Sent Events at write time; they are not a polling query against
the database.
All of these are set inside AddNafasServer(options => { ... }).
| Option | Default | Notes |
|---|---|---|
Provider | DatabaseProvider.Sqlite | Sqlite or SqlServer |
ConnectionString | Data Source=nafas.db (SQLite only) | Set directly, or use ConnectionStringName instead |
ConnectionStringName | (none) | Resolves from appsettings.json's ConnectionStrings section first, then classic web.config's <connectionStrings> |
Schema | dbo | SQL Server only: keeps Nafas's tables under a named schema, separate from the rest of your database |
RetentionDays | 30 | How long logs/metrics/traces are kept before a background sweep deletes them |
AlertWebhookUrl | (none) | Where alert incidents are POSTed as JSON. Alert rules and incidents still work without it; nothing gets delivered anywhere until it's set |
ServiceName | IHostEnvironment.ApplicationName | Overrides the service name stamped on everything ingested |
Authorize | null (local requests only) | Gates every request to the dashboard; see Security |
UseNafasDashboard(path) takes the mount path for the dashboard UI itself
(default "/nafas"). Pick any path your app isn't already using.
The dashboard shows logs, traces, and metrics, which routinely carry
sensitive data (request bodies, stack traces, connection strings inside
exception messages). By default, UseNafasDashboard only serves requests
that come from the local machine, the same default posture as
Hangfire's own dashboard.
Every other request gets a 403.
builder.Services.AddNafasServer(options =>
{
// Runs after your own auth middleware, if you have one registered
// earlier in the pipeline -- httpContext.User is already populated.
options.Authorize = httpContext => httpContext.User.IsInRole("Admin");
// Or, deliberately, open to everyone (not recommended without your
// own auth in front of it):
// options.Authorize = _ => true;
});
If you don't set Authorize and the dashboard doesn't load from where you
expect (a phone on the same network, a teammate's machine, behind a
reverse proxy), this is why. Set it explicitly once you know who should
be allowed in.
UseNafasDashboard needs an IApplicationBuilder to mount onto. A plain
WinForms, WPF, or console/worker app has no ASP.NET Core pipeline of its
own to provide one. Nafas.Observability.Server is a separate, optional
package for exactly that case: it runs a second, independent HTTP listener
(on its own port) that serves the same dashboard against the same running
app's real data. No Kestrel, no OWIN, just a minimal HTTP/1.1 server
directly on TcpListener (see
Nafas.Observability.Server/Nafas.Observability.Server.csproj's
own comment for why: the last version of Kestrel ever published as a plain
NuGet package is 2.3.13 from 2019, with no HTTP/2 and no security patches
since, and System.Net.HttpListener (the BCL's own listener) turned out
not to be usable for the LAN case either: on Windows it's backed by
HTTP.sys, which refuses to bind any prefix other than "localhost" unless
the process runs elevated or a URL ACL was reserved beforehand, verified
empirically). Being plain netstandard2.0 like the core package itself,
this also works from classic .NET Framework 4.6.1+ apps, not just modern
.NET. Confirmed against a real net48 console host, not just the TFM
compatibility rules, in
Nafas.Observability.Server.NetFrameworkSample
(see Requirements for the one net48-specific gotcha it
also caught, around SQLite and PlatformTarget).
dotnet add package Nafas.Observability.Server
services.AddNafasServer(); // same as always -- storage + ingestion
services.AddNafasHttpServer(o =>
{
o.Enabled = true; // opt-in; does nothing while false
o.Port = 5099;
o.AccessMode = NafasHttpServerAccessMode.LocalhostOnly; // or .Lan
});
then open http://localhost:5099/nafas. NafasHttpServerOptions.Enabled is
only read once at startup. To start/stop/reconfigure it later (e.g. a
settings screen's "enable dashboard" checkbox), resolve NafasHttpServer
from DI and call its StartAsync/StopAsync directly; see
Nafas.Observability.Server.Sample/
for a worked WPF example.
AccessMode only controls which network interface is bound:
LocalhostOnly (the default) binds loopback only, Lan binds every
interface so other devices on the network can reach the port via this
machine's own IP, with no administrator rights required either way (unlike
System.Net.HttpListener, verified empirically on both). It does not by
itself relax NafasServerOptions.Authorize (still local-requests-only by
default, see Security): set Authorize explicitly too if
Lan should actually let those requests through, otherwise they still get
a 403.
One request per connection, not HTTP/1.1 keep-alive. This listener is a
local/LAN admin tool, not a public high-throughput API, so giving up
connection reuse (an extra TCP handshake per dashboard API call,
imperceptible on localhost/LAN) removes an entire dimension of correctness
surface instead. See
Nafas.Observability.Server/Internal/NafasRawHttpConnection.cs's
own comment.
Threshold and absence rules are defined from the dashboard itself (Alerts page). No code required to create one. A background evaluator polls every 30 seconds and fires only on state transitions (an incident opens when a rule first goes out of bounds, and resolves when it recovers) so a rule that stays broken for an hour doesn't re-notify every cycle.
Nafas does not send email, SMS, or Slack messages itself. When
AlertWebhookUrl is set, every open or resolve event is POSTed there as
JSON, and your own system takes it from there:
{
"ruleId": 12,
"ruleName": "High error rate",
"ruleType": "threshold",
"status": "open",
"detail": "error_rate is 8.4, threshold is 5",
"triggeredAtUtc": "2026-01-15T09:32:00Z",
"resolvedAtUtc": null
}
A failed webhook delivery is logged and retried on the next transition; it never fails the request pipeline, and the incident still exists in the dashboard's own incident list regardless of whether delivery succeeded.
Nafas has no concept of a central collector: each instance that calls
AddNafasServer() ingests and stores its own process's telemetry. Two
common setups:
ServiceName
resolves to IHostEnvironment.ApplicationName.ConnectionString/ConnectionStringName at the same
database and give each a distinct ServiceName. All of them write to
the same tables and are distinguishable in every dashboard view by
service name; any instance's /nafas mount shows data from all of
them.SQLite is a single-file, single-process store and is not meant to be shared across instances. Use SQL Server for that case.
netstandard2.0 for the broadest possible reach on
the consuming app's side: .NET Core 2.0+, .NET 5+, or classic .NET
Framework 4.6.1+ can all reference it (ConnectionStringName's
classic web.config support exists for exactly that last case).UseNafasDashboard requires
IApplicationBuilder. For WinForms/WPF/console apps, see
Nafas.Observability.Server
instead.memoryUsage (one of several resource metrics; logs, traces, CPU
usage, and alerting are all unaffected either way) is read differently
depending on the host, in this order:
GC.GetGCMemoryInfo(),
which is container-aware. It reflects a cgroup or Docker memory limit
when one is set, not just physical host RAM.GlobalMemoryStatusEx API (the same one
.NET Framework apps have always used for this, no managed equivalent
exists there). This reports whole-machine physical memory, not
container/Job-Object-aware, so a process capped by a Job Object
memory limit will under-report here.GC.GetGCMemoryInfo() (practically:
.NET Core 2.x on Linux/macOS): reports 0. This path is explicitly
gated to Windows only, so a Linux container never attempts a
kernel32.dll call in the first place.PlatformTarget.
netstandard2.0 compatibility means .NET Framework can reference this
package, but the SQLite native driver still needs to know which
architecture to load. On .NET Core/.NET 5+, the runtime's own RID-graph
resolves this automatically regardless of how the project was built; on
classic .NET Framework there's no such mechanism, so an AnyCPU build
can copy a native e_sqlite3.dll that doesn't match the process's actual
bitness at run time, failing with Library e_sqlite3 not found / %1 is not a valid Win32 application. Set <PlatformTarget>x64</PlatformTarget>
(or x86, matching your deployment) and <Prefer32Bit>false</Prefer32Bit>
explicitly in the consuming to fix it. Verified against a real
net48 console host in
,
see that project's own comment. SQL Server storage isn't
affected (no native driver to resolve this way).For anyone evaluating this beyond the quick start:
Ingestion/) is entirely in-process: an
ILoggerProvider, an ActivityListener, and a MeterListener, each
writing into a bounded per-signal Channel<T> (capacity 20,000,
drop-on-full). A single hosted service drains all three, batches
writes (up to 500 records or every second, whichever comes first), and
persists them.Storage/) is a thin interface
(INafasIngestionWriter/INafasQueryStore) with two concrete
implementations, SQLite and SQL Server. No ORM, parameterized SQL,
schema created and migrated at startup.NafasAlertEvaluationHostedService) is a polling
BackgroundService reusing the same query layer the dashboard's KPI
views use, tracking one open-incident-per-rule at a time.NafasLiveFeed) fed by the ingestion writer and alert evaluator, read
by the dashboard's SSE endpoints. Nothing here is a polling query.ClientApp/) is a Vue 3 + Vite SPA (PrimeVue,
Pinia, Chart.js, vue-i18n for English/Persian), built ahead of time and
embedded directly into the assembly as embedded resources. A consumer
installing the package gets the whole UI with no separate static-file
deployment step, and no Node.js runtime on the host machine.api/* surface (NafasDashboardEndpoints.cs) is a small
hand-rolled router rather than ASP.NET Core's IEndpointRouteBuilder,
because endpoint routing has been FrameworkReference-only since
ASP.NET Core 3.0 and is unavailable to a netstandard2.x class
library.Nafas.Observability/: the package itself.Nafas.Dashboard.TestHost/: a minimal
ASP.NET Core app used to manually verify the package end to end; not a
usage example to copy patterns from.Nafas.Observability.Server/: the
optional standalone HTTP listener package for non-ASP.NET Core apps (no
Kestrel, no OWIN); see Desktop and other non-ASP.NET Core apps.Nafas.Observability.Server.Sample/:
a WPF app demonstrating it, with a settings-screen-style checkbox to
enable/disable the dashboard server, pick its port, and choose
localhost-only vs. LAN access at runtime.Nafas.Observability.Server.NetFrameworkSample/:
a plain net48 console app; not a usage pattern to copy (a real .NET
Framework consumer wouldn't normally need the Generic Host boilerplate
shown here), but an actual classic .NET Framework process used to
empirically verify the whole stack (storage, ingestion, and the
TcpListener-based HTTP server) rather than just relying on
netstandard2.0's TFM compatibility claim.For normal use of the published package, no Node.js/npm step is needed at
all. The dashboard UI is already built and committed under
Nafas.Observability/wwwroot/. Just build the solution:
dotnet build nafas.observability.sln
To try the package locally end to end:
cd Nafas.Dashboard.TestHost
dotnet run
then open http://localhost:<port>/nafas.
To change the dashboard UI itself:
cd Nafas.Observability/ClientApp
npm install # first time only
npm run build # output goes to ClientApp/dist/
# then replace (not merge) the contents of ../wwwroot/ with dist/'s contents
then dotnet build again so the updated files are re-embedded.
.github/workflows/publish.yml does this same rebuild-and-sync step from
source on every publish, so a tagged release never ships a stale UI.
Feature-complete for the free tier. Logs, metrics, traces, alerting,
the embedded dashboard, and the standalone desktop/console server
(Nafas.Observability.Server) are all shipped. No further feature work
is planned for this scope. From here, changes to the free tier are bug
fixes and hardening, not new capability.
Still pre-1.0 (see the NuGet badges at the top of this file for the exact
versions), and that status is about proof, not scope: real ingestion,
storage, dashboard, and alert evaluation all work and are exercised
through Nafas.Dashboard.TestHost, but neither package has yet run in a
real production deployment or been through a real upgrade cycle. The
public API surface (NafasServerOptions, UseNafasDashboard) is expected
to stay stable regardless.
FSL-1.1-ALv2: free to use, modify and redistribute for any purpose other than building a competing product or service; each release converts to the Apache License, Version 2.0 two years after it ships. See LICENSE.md.
.csproj.csproj