# 1. 0 के लिए: उममी बनाने के लिए तैयार.

<!-- category -- C#, Umami, Analytics, Open Source -->
<datetime class="hidden">2025-11-20T14:30</datetime>

## परिचय

जब मैं पहली बार एकीकृत करूँ [उममी अव्यादेश](https://umami.is/) मेरे ब्लॉग मंच में, मैं जल्दी ही एक निराश वास्तविकता में दौड़ता हूँ: उममी का एपीआई दस्तावेज़ है... चलो दान कर दें और इसे "मिनिर्म" कहते हैं. त्रुटि संदेश सबसे अच्छे पर रोना कर रहे हैं, सबसे बुरी तरह से बुरे पर. उदाहरणों के बीच के संस्करण बदल रहे हैं. और यहाँ तक कि मुझे "रोच" पर शुरू नहीं मिलता है.

तो मैंने बनाया [उममी.](https://github.com/scottgal/mostlylucidweb/tree/main/Umami.Net) - बस एक सरल HTTPwper के रूप में नहीं है, लेकिन एक उत्पादन ग्राहक तैयार लाइब्रेरी के रूप में जो सभी उमामा के लिए भुगतान करता है. के रूप में मैं एक 1. 0 रिलीज की ओर काम कर रहा है, मैं तीन महत्वपूर्ण क्षेत्रों पर ध्यान केंद्रित किया है:

1. **सहायक त्रुटि संदेशों के साथ सामान्यीकरण**
2. **रॉबन जाँच इन्फ्रास्ट्रक्चर**
3. **वास्तविक संसार के दृश्‍य के लिए अनुग्रहपूर्ण त्रुटि**

मुझे इस पुस्तकालय उत्पादन को तैयार करने के माध्यम से आप चलते हैं.

[![लागू नहीं](https://img.shields.io/nuget/v/Umami.Net.svg?style=flat-square)](https://www.nuget.org/packages/Umami.Net/)
[![लाइसेंस:](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
[![.नेट](https://img.shields.io/badge/.NET-9.0-purple?style=flat-square)](https://dotnet.microsoft.com/)

[TOC]

## समस्या: उममी का दस्तावेज़ीकरण जीपी

यहाँ आप क्या कर रहे हैं जब आप सीधे उममी के एपीआई के साथ काम कर रहे हैं:

- **कोई इनपुट वैध नहीं** - एक गलत जीयूआई भेजें?
- **शोर - शराबा जवाब** - बोट पता चला? `"beep boop"`.. यही है.
- **परिवर्तनों को ब्रेक किया जा रहा है** - पैरामीटर संस्करण के बीच का पुनर्नामकरण (e)`path` वी. `url`, `hostname` वी. `host`)
- **समय- चिह्न** अच्छा भाग्य यह बाहर पता लगाने के लिए.
- **जेएलटी जवाब** कभी-कभी पूर्ण भुगतान, कभी कभी कभी-कभी एक आगंतुक आईडी. कोई दस्तावेज जब या क्यों.

यह एक त्वरित निर्माता के लिए ठीक है, लेकिन उत्पादन के लिए? आप कुछ बेहतर की जरूरत है.

## गार्डियन्स: संदर्भ के साथ तेज असफल

सबसे बुरा बग जो चुपचाप असफल हो जाता है. उममी शुरू होने से पहले वे उत्पादन में समस्या पैदा कर सकते हैं.

### कॉन्फ़िगरेशन वेलिडेशन

```csharp
public static void ValidateSettings(UmamiClientSettings settings)
{
    // Guard: UmamiPath is required
    if (string.IsNullOrEmpty(settings.UmamiPath))
        throw new ArgumentNullException(settings.UmamiPath,
            "UmamiUrl is required");

    // Guard: UmamiPath must be valid URI
    if (!Uri.TryCreate(settings.UmamiPath, UriKind.Absolute, out _))
        throw new FormatException(
            "UmamiUrl must be a valid Uri");

    // Guard: WebsiteId is required
    if (string.IsNullOrEmpty(settings.WebsiteId))
        throw new ArgumentNullException(settings.WebsiteId,
            "WebsiteId is required");

    // Guard: WebsiteId must be valid GUID
    if (!Guid.TryParseExact(settings.WebsiteId, "D", out _))
        throw new FormatException(
            "WebSiteId must be a valid Guid");
}
```

यह आपके में प्रारंभ में चलाता है `Program.cs`. अगर आपका विन्यास गलत है, तो आप तुरंत नहीं जानते कि पहली बार जब पहली बार कौन - सी घटना भेजने की कोशिश की जाती है ।

### मदद युक्त सुझावों के साथ वेलिडेशन निवेदित

लेकिन वास्तविक जादू क्वैरी वाक्यांश सहायक में है. इन त्रुटि संदेशों को जाँचें:

```csharp
public static string ToQueryString(this object obj)
{
    if (obj == null)
    {
        throw new ArgumentNullException(nameof(obj),
            "Cannot convert null object to query string. " +
            "Suggestion: Ensure you create and populate a request object " +
            "before calling ToQueryString().");
    }

    foreach (var property in objectType.GetProperties())
    {
        if (attribute.IsRequired)
        {
            if (propertyValue == null)
            {
                throw new ArgumentException(
                    $"Required parameter '{propertyName}' " +
                    $"(property '{property.Name}') cannot be null. " +
                    $"Suggestion: Set the {property.Name} property " +
                    $"on your {objectType.Name} object...",
                    property.Name);
            }

            // For strings, check for empty/whitespace
            if (propertyValue is string strValue &&
                string.IsNullOrWhiteSpace(strValue))
            {
                throw new ArgumentException(
                    $"Required parameter '{propertyName}' " +
                    $"cannot be empty or whitespace. " +
                    $"Suggestion: Set {property.Name} to a valid non-empty value.",
                    property.Name);
            }
        }
    }
}
```

ध्यान दें `Suggestion:` उपसर्ग? प्रत्येक त्रुटि संदेश आपको बताता है **क्या गलत हो गया** और **कैसे इसे ठीक करें**यह दस्तावेज़ उममी को प्रदान किया जाना चाहिए था.

### तिथि सीमा वेलिडेशन

पुस्तकालय आपके लिए इन ग़लतियों को पहचान लेता है:

```csharp
public DateTime StartAtDate
{
    get => _startAtDate;
    set
    {
        if (_endAtDate != default && value > _endAtDate)
        {
            throw new ArgumentException(
                $"StartAtDate ({value:O}) must be before EndAtDate ({_endAtDate:O}). " +
                "Suggestion: Set StartAtDate to an earlier date or adjust EndAtDate.",
                nameof(StartAtDate));
        }
        _startAtDate = value;
    }
}

public virtual void Validate()
{
    if (StartAtDate == default)
    {
        throw new InvalidOperationException(
            "StartAtDate is required. " +
            "Suggestion: Set StartAtDate to a valid date " +
            "(e.g., DateTime.UtcNow.AddDays(-7) for last 7 days).");
    }
}
```

## उममी के क्लैर्क को हैंडल करें

### "Biob" समस्या

उममी का बॉटल सादा पाठ प्रतिक्रिया बताता है: `"beep boop"`JSON. नहीं एक उचित स्थिति कोड. बस... बीप.

यहाँ उममी यह कैसे संभालता है:

```csharp
public async Task<UmamiDataResponse> DecodeResponse(HttpResponseMessage response)
{
    var responseString = await response.Content.ReadAsStringAsync();

    // Handle bot detection
    if (responseString.Contains("beep") && responseString.Contains("boop"))
    {
        logger.LogWarning("Bot detected - data not stored in Umami");
        return new UmamiDataResponse(ResponseStatus.BotDetected);
    }

    // Handle JWT response
    try
    {
        var jwtPayload = DecodeJwt(responseString);
        return new UmamiDataResponse(ResponseStatus.Success, jwtPayload);
    }
    catch (Exception e)
    {
        logger.LogError(e, "Failed to decode response");
        return new UmamiDataResponse(ResponseStatus.Failed);
    }
}
```

आपका कोड साफ enum हो जाता है:

```csharp
public enum ResponseStatus
{
    Failed,
    BotDetected,
    Success
}
```

और कोई अजीब प्रतिक्रियाओं की जाँच नहीं कर रहा है - बस स्थिति की जाँच करें.

### पैरामीटर नाम परिवर्तन

उममी का नाम एपीआई संस्करणों के बीच पैरामीटरों. क्या उन्होंने इसे लिखा? बेशक नहीं. पुस्तकालय दोनों को संभालता है:

```csharp
// Support both old and new parameter names
request.Path = queryParams["path"] ?? queryParams["url"];
request.Hostname = queryParams["hostname"] ?? queryParams["host"];
```

### समय- चिह्न परिवर्तन

उममी समय- चिह्नों के लिए टैक्सियों का उपयोग करती है. यहाँ एक सहायक है जो इसे दर्दहीन बनाता है:

```csharp
public static long ToMilliseconds(this DateTime dateTime)
{
    var dateTimeOffset = new DateTimeOffset(dateTime.ToUniversalTime());
    return dateTimeOffset.ToUnixTimeMilliseconds();
}
```

अब आप सामान्य के साथ काम कर सकते हैं `DateTime` वस्तुओं और पुस्तकालय परिवर्तन संभाल करते हैं.

## पृष्ठभूमि जिसमें चैनल्स के साथ प्रोसेस किया गया है

विश्लेषणात्मक आपके अनुप्रयोग को कभी ब्लॉक नहीं करना चाहिए. उममी. `System.Threading.Channels`:

```csharp
public class UmamiBackgroundSender : IHostedService
{
    private readonly Channel<UmamiPayload> _channel;
    private readonly UmamiClient _client;

    public async Task Track(string eventName,
        string? url = null,
        UmamiEventData? data = null)
    {
        var payload = new UmamiPayload
        {
            Website = _settings.WebsiteId,
            Name = eventName,
            Url = url ?? string.Empty,
            Data = data
        };

        // Non-blocking write to channel
        await _channel.Writer.WriteAsync(payload);
    }

    private async Task ProcessQueue(CancellationToken stoppingToken)
    {
        await foreach (var payload in _channel.Reader.ReadAllAsync(stoppingToken))
        {
            try
            {
                await _client.Send(payload);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to send event to Umami");
            }
        }
    }
}
```

घटनाएँ स्मृति में कतार हैं और अतुल्यकालिक रूप से प्रोसेस की गई हैं. आपका वेब अनुरोध पल भर में, पृष्ठभूमि में होता है.

## पोली के साथ नीति फिर से कोशिश करें

नेटवर्क असफल हो गया. लाइब्रेरी "%s" कॉल के लिए पोल्स का प्रयोग करता है:

```csharp
public static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    var delay = Backoff.DecorrelatedJitterBackoffV2(
        TimeSpan.FromSeconds(1),
        retryCount: 3);

    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .OrResult(msg => msg.StatusCode == HttpStatusCode.ServiceUnavailable)
        .WaitAndRetryAsync(delay);
}
```

विशेषज्ञ असफलता और ५०३ ग़लतियाँ स्वचालित रूप से पीठ बन्द करने में बाधा डालती हैं ।

## स्वतः पुनः प्रारंभ करें

जब एक पारानिक डाटा लाया जाता है (सिर्फ घटनाओं को नहीं भेजते हैं), आपको प्रमाणीकरण की आवश्यकता है. लाइब्रेरी चिह्न स्वचालित रूप से भटकता है:

```csharp
public async Task<UmamiResult<StatsResponseModel>> GetStats(StatsRequest statsRequest)
{
    var response = await _httpClient.GetAsync(url);

    // Token expired? Re-authenticate and retry
    if (response.StatusCode == HttpStatusCode.Unauthorized)
    {
        await _authService.Login();
        return await GetStats(statsRequest); // Recursive retry
    }

    // Parse and return
    var content = await response.Content.ReadFromJsonAsync<StatsResponseModel>();
    return new UmamiResult<StatsResponseModel>(
        response.StatusCode,
        response.ReasonPhrase ?? string.Empty,
        content);
}
```

आपको टोकन प्रबंधन के बारे में कभी नहीं सोचना है - यह सिर्फ काम करता है.

## इनफ़ॉयरस्ट्रक्चर को जाँच किया जा रहा है

उत्पाद तैयार कोड व्यापक जाँच की आवश्यकता है. यहाँ मैं क्या निर्मित है:

### ताला खोलने के लिए नक़ल करें (a)

माइक्रोसॉफ्टएस का प्रयोग कर रहा है `FakeLogger` पैकेज, जाँच लॉगिंग बर्ताव की जांच कर सकते हैं:

```csharp
[Fact]
public async Task Login_Success_LogsMessage()
{
    // Arrange
    var fakeLogger = new FakeLogger<AuthService>();
    var authService = new AuthService(httpClient, settings, fakeLogger);

    // Act
    await authService.Login();

    // Assert
    var logs = fakeLogger.Collector.GetSnapshot();
    Assert.Contains("Login successful", logs.Select(x => x.Message));
}
```

### मनपसंद नकली एचटीटीपी हैंडलर

ब्रेकिंग ऑपरेशन मुश्किल है. यहाँ एक पैटर्न है जो इस्तेमाल किया जा रहा है `TaskCompletionSource`:

```csharp
[Fact]
public async Task BackgroundSender_ProcessesEventAsynchronously()
{
    var tcs = new TaskCompletionSource<bool>();

    var handler = EchoMockHandler.Create(async (message, token) =>
    {
        try
        {
            // Assert the request was sent correctly
            var payload = await message.Content.ReadFromJsonAsync<UmamiPayload>();
            Assert.Equal("test-event", payload.Name);

            tcs.SetResult(true); // Signal test completion
            return new HttpResponseMessage(HttpStatusCode.OK);
        }
        catch (Exception e)
        {
            tcs.SetException(e);
            return new HttpResponseMessage(HttpStatusCode.InternalServerError);
        }
    });

    // Track event
    await backgroundSender.Track("test-event");

    // Wait for background processing with timeout
    var completedTask = await Task.WhenAny(tcs.Task, Task.Delay(1000));
    if (completedTask != tcs.Task)
        throw new TimeoutException("Event was not processed within timeout");

    await tcs.Task; // Throw if assertions failed
}
```

यह पैटर्न सुनिश्चित करता है:

- पृष्ठभूमि घटनाओं को वास्तव में प्रोसेस किया जाता है
- प्रक्रिया उचित समय के भीतर पूर्ण
- उपहास हैंडलर में सूचना सही प्रकार से दी जाती है

### चूक जाने वाले जाँच कवरेज

जाँच सूट कवर:

- AMS कॉन्फ़िगरेशन वैध (अवैध जीयूआईडी, यूआरएल गुम है)
- _RW घटना इसके साथ और इसके आँकड़ा के बिना ट्रैक की जा रही है
- OCT पृष्ठ दृश्य ट्रैकिंग
- यूसीएस उपयोक्ता पहचान
- 2880 बोट जाँच हैंडल करें
- डब्ल्यूएचओटी प्रतिक्रिया दे रही है
- समय समाप्ति के साथ पृष्ठभूमि प्रक्रिया
- DTMF दिनांक परिसर वैध करने की छूट देता है
- एसईटी क्वैरी वाक्यांश तैयार करने में अक्षम.
- सत्यापन और टोकन
- कामों की दोबारा जाँच की जा सकती है

## वास्तविक विश्वव्यापी उपयोग

यहाँ कैसे सरल है एक कि एक अनजानी में उपयोग करने के लिए है. NENT अनुप्रयोग:

### प्रोग्राम में सेटअप करें

```csharp
builder.Services.SetupUmamiClient(builder.Configuration);
```

पुस्तकालय आपका लिखा हुआ है `appsettings.json`:

```json
{
  "Analytics": {
    "UmamiPath": "https://analytics.yoursite.com",
    "WebsiteId": "your-website-guid"
  }
}
```

### ट्रैक घटनाएँ

```csharp
public class HomeController : Controller
{
    private readonly UmamiBackgroundSender _umami;

    public HomeController(UmamiBackgroundSender umami)
    {
        _umami = umami;
    }

    public IActionResult Index()
    {
        // Non-blocking event tracking
        await _umami.TrackPageView("/", "Home Page");

        return View();
    }

    [HttpPost]
    public async Task<IActionResult> Subscribe(string email)
    {
        // Track with custom data
        await _umami.Track("newsletter-signup",
            data: new UmamiEventData
            {
                { "source", "homepage" },
                { "email_domain", email.Split('@')[1] }
            });

        return RedirectToAction("ThankYou");
    }
}
```

### मेटा डाटा प्राप्त करें

```csharp
public class AnalyticsDashboardController : Controller
{
    private readonly IUmamiDataService _umamiData;

    public async Task<IActionResult> Stats()
    {
        var request = new StatsRequest
        {
            StartAtDate = DateTime.UtcNow.AddDays(-30),
            EndAtDate = DateTime.UtcNow
        };

        var result = await _umamiData.GetStats(request);

        if (result.Status == HttpStatusCode.OK)
        {
            var stats = result.Data;
            // stats.Visitors, stats.PageViews, stats.BounceRate, etc.
            return View(stats);
        }

        return View("Error");
    }
}
```

## 1. 0 के लिए अगला क्या है?

पुस्तकालय इस बहुत ही ब्लॉग पर उत्पादन में पूर्ण है तथा युद्ध की जांच है. 1. 0 रिलीज से पहले, मैं ध्यान केंद्रित कर रहा हूँ:

- सबको तैयार करने के लिए बाइंडिंग.
- © एनयू पैकेज प्रकाशन
- परफ़ॉर्मेंस परफ़ॉर्मेंसमार्क्स
- आम लोगों के लिए अतिरिक्त आराम - सामग्री

## कंटेनमेंट

एक उत्पाद तैयार पुस्तकालय सिर्फ एक एपीआई को खोलने के बारे में नहीं है - यह एक अनुभव बनाने के बारे में है कि **बेहतर** एपीआई सीधे उपयोग करने से पहले. उममी. उममी के दस्तावेज़ों के लिए साली भुगतान करता है के साथ:

- **वेलिडेशन समझाता है कि क्या गलत हो गया था और कैसे इसे ठीक करें**
- **quke Naka के व्यवहार का अनुग्रह प्रदान**
- **गंभीरता से जाँच करें जो साबित करता है कि यह काम करता है**
- **पृष्ठभूमि प्रक्रिया है कि आपके एप्पल को ब्लॉक नहीं करता**
- **स्वचालित पुनः प्रारंभ करने के दौरान सिंकिंग में त्रुटि**

अगर आप एक में उममी एथेमी का उपयोग कर रहे हैं... ... NET अनुप्रयोग, मैं आप की कोशिश करने के लिए प्यार होगा [उममी.](https://github.com/scottgal/mostlylucidweb/tree/main/Umami.Net)..यह खुला स्रोत है, बहुत परीक्षण, और अपने जीवन को आसान बनाने के लिए बनाया है.

सवाल या सुझाव समझे?