Adición de un cliente C# Umami Tracking (Español (Spanish))

Adición de un cliente C# Umami Tracking

Sunday, 18 August 2024

//

10 minute read

El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer.

En un post anterior añadimos un cliente para extraer El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer.. En este post añadiremos un cliente para enviar datos de seguimiento a Umami desde una aplicación CM SK1 El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer. es un servicio de análisis ligero que puede ser propio-hostedM SK1 Es una gran alternativa a Google Analytics y está enfocado en la privacidad. Sin embargo, por defecto sólo tiene un cliente de nódulos para rastrear datos (y incluso entonces no es GREATM SK2 Así que he decidido escribir un cliente C# para rastrer datosMSC4

Nota: He actualizado esto ahora, Yo'actualizaré el post del blog más tarde M SK2 Al momento de ser 26/08/2024

El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer.

El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer. puede ver cómo yo lo doy aquí.

El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer.

Puede ver todos los códigos de fuente para el cliente a) Adopte todas las medidas necesarias para garantizar la igualdad de oportunidades entre hombres y mujeres en todos los ámbitos de la vida, incluida la educación;.

Esto utiliza las configuraciones que he definido en mi appsettings.json El Comité recomienda que el Estado Parte adopte todas las medidas necesarias para garantizar la igualdad de oportunidades entre los hombres y las mujeres en materia de empleo, incluida la eliminación del trabajo forzoso.

 "Analytics":{
   "UmamiPath" : "https://umamilocal.mostlylucid.net",
   "WebsiteId" : "32c2aa31-b1ac-44c0-b8f3-ff1f50403bee",
    "UmamiScript" : "getinfo"
 },

Como el API de la pista no es autenticado no he agregado ninguna autenticación al cliente.

El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer.

Para configurar el cliente I've agregado mi método de extensión habitual con se llama desde su Program.cs El Comité recomienda que el Estado Parte adopte todas las medidas necesarias para garantizar la igualdad de oportunidades entre los hombres y las mujeres en materia de empleo, incluida la eliminación del trabajo forzoso.

services.SetupUmamiClient(config);

Esto proporciona una forma sencilla de conectarse en el UmamiClient a su aplicación.

El código de abajo muestra el método de configuración.

   public static void SetupUmamiClient(this IServiceCollection services, IConfiguration config)
    {
       var umamiSettings= services.ConfigurePOCO<UmamiClientSettings>(config.GetSection(UmamiClientSettings.Section));
       if(string.IsNullOrEmpty( umamiSettings.UmamiPath)) throw new Exception("UmamiUrl is required");
       if(string.IsNullOrEmpty(umamiSettings.WebsiteId)) throw new Exception("WebsiteId is required");
       services.AddTransient<HttpLogger>();
        services.AddHttpClient<UmamiClient>((serviceProvider, client) =>
            {
                 umamiSettings = serviceProvider.GetRequiredService<UmamiClientSettings>();
            client.DefaultRequestHeaders.Add("User-Agent", $"Mozilla/5.0 Node/{Environment.Version}");
            client.BaseAddress = new Uri(umamiSettings.UmamiPath);
        }).SetHandlerLifetime(TimeSpan.FromMinutes(5))  //Set lifetime to five minutes
        .AddPolicyHandler(GetRetryPolicy())
       #if DEBUG 
        .AddLogger<HttpLogger>();
        #else
        ;
        #endif
        
        services.AddHttpContextAccessor();
    }
    
    static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    {
        return HttpPolicyExtensions
            .HandleTransientHttpError()
            .OrResult(msg =>  msg.StatusCode == HttpStatusCode.ServiceUnavailable)
            .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
    }

Como usted puede ver, esto hace lo siguiente:

  1. Establecer el objeto de configuración
  2. Véase Documentos Oficiales de la Asamblea General, quincuagésimo sexto período de sesiones, Suplemento No. 10 (A/56/10), párr.
  3. Añadir un logger (si en modo de debugging)
  4. Establecer el cliente Http con la dirección de base y una política de repetición.

El Comité recomienda al Estado Parte que incluya en su próximo informe periódico datos estadísticos desglosados por sexo, edad y origen étnico.

El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer. UmamiClient es bastante simple. Tiene un método central Send que envía los datos de seguimiento al servidor Umami.

    public async Task<HttpResponseMessage> Send(UmamiPayload payload, string type = "event")
    {
        var jsonPayload = new { type, payload };
        logger.LogInformation("Sending data to Umami {Payload}", JsonSerializer.Serialize(jsonPayload, options));
        var response= await client.PostAsJsonAsync("/api/send", jsonPayload, options);
        if(!response.IsSuccessStatusCode)
        {
           logger.LogError("Failed to send data to Umami {Response}, {Message}", response.StatusCode, response.ReasonPhrase);
        }
        else
        {
            var content = await response.Content.ReadAsStringAsync();
            logger.LogInformation("Successfully sent data to Umami {Response}, {Message} {Content}", response.StatusCode, response.ReasonPhrase, content);
        }
        return response;
    }

Como verá' esto utiliza un objeto llamado UmamiPayload que contiene todos los parámetros posibles para seguir las solicitudes en Umami.

public class UmamiPayload
{
    public string Website { get; set; }=string.Empty;
    public string Hostname { get; set; }=string.Empty;
    public string Language { get; set; }=string.Empty;
    public string Referrer { get; set; }=string.Empty;
    public string Screen { get; set; }=string.Empty;
    public string Title { get; set; }   =string.Empty;
    public string Url { get; set; } =string.Empty;
    public string Name { get; set; } =string.Empty;
    public UmamiEventData? Data { get; set; }
}

public class UmamiEventData : Dictionary<string, object> { }

El único campo requerido es Website que es el sitio web id. El resto son opcionales ( pero Url Es realmente útil!).

En el cliente tengo un método llamado GetPayload() que envía llena este objeto de carga automáticamente con la información de la solicitud (usando el injeto IHttpContextAccessor).


public class UmamiClient(HttpClient client, ILogger<UmamiClient> logger, IHttpContextAccessor accessor, UmamiClientSettings settings)...

    private UmamiPayload GetPayload(string? url = null, UmamiEventData? data = null)
    {
        // Initialize a new UmamiPayload object
        var payload = new UmamiPayload
        {
            Website = settings.WebsiteId,
            Data = data ?? new UmamiEventData(),
            Url = url ?? "" // Default URL to empty string if null
        };

        // Check if HttpContext is available
        if (accessor.HttpContext != null)
        {
            var context = accessor.HttpContext;
            var headers = context.Request.Headers;

            // Fill payload details from HttpContext and headers
            payload.Hostname = context?.Request.Host.Host ?? "";  // Default to empty string if null
            payload.Language = headers?["Accept-Language"].ToString() ?? "";  // Safely retrieve Accept-Language header
            payload.Referrer = headers?["Referer"].ToString() ?? "";  // Safely retrieve Referer header
            payload.Screen = headers?["User-Agent"].ToString() ?? "";  // Safely retrieve User-Agent header
            payload.Title = headers?["Title"].ToString() ?? "";  // Safely retrieve Title header
            payload.Url = string.IsNullOrEmpty(url) ? context.Request.Path.ToString() : url;  // Use the passed URL or fallback to the request path
        }

        return payload;
    }

Esto es entonces utilizado por otros métodos utilitarios que dan una interfaz más agradable para estos datos.

    public async Task<HttpResponseMessage> TrackUrl(string? url="", string? eventname = "event", UmamiEventData? eventData = null)
    {
        var payload = GetPayload(url);
        payload.Name = eventname;
        return await Track(payload, eventData);
    }

    public async Task<HttpResponseMessage> Track(string eventObj, UmamiEventData? eventData = null)
    {
        var payload = new UmamiPayload
        {
            Website = settings.WebsiteId,
            Name = eventObj,
            Data = eventData ?? new UmamiEventData()
        };

        return await Send(payload);
    }

    public async Task<HttpResponseMessage> Track(UmamiPayload eventObj, UmamiEventData? eventData = null)
    {
        var payload = eventObj;
        payload.Data = eventData ?? new UmamiEventData();
        payload.Website = settings.WebsiteId;
        return await Send(payload);
    }

    public async Task<HttpResponseMessage> Identify(UmamiEventData eventData)
    {
        var payload = new UmamiPayload
        {
            Website = settings.WebsiteId,
            Data = eventData ?? new()
        };

        return await Send(payload, "identify");
    }

Esto le permite seguir los eventos, URLs y identificar a los usuarios.

El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer.

En el futuro planeo hacer esto en un paquete NuGet. Testing para que tengo una entrada en el Umami.Client.csproj archivo que genera un paquete de nueva versión 'preview' cuando se construye en modo de debuggingM SK2

   <Target Name="NugetPackAutoVersioning" AfterTargets="Build" Condition="'$(Configuration)' == 'Debug'">
    <!-- Delete the contents of the target directory -->
    <RemoveDir Directories="$(SolutionDir)nuget" />
    <!-- Recreate the target directory -->
    <MakeDir Directories="$(SolutionDir)nuget" />
    <!-- Run the dotnet pack command -->
    <Exec Command="dotnet pack -p:PackageVersion=$([System.DateTime]::Now.ToString(&quot;yyyy.MM.dd.HHmm&quot;))-preview -p:V --no-build --configuration $(Configuration) --output &quot;$(SolutionDir)nuget&quot;" />
    <Exec Command="dotnet nuget push $(SolutionDir)nuget\*.nupkg --source Local" />
    <Exec Command="del /f /s /q $(SolutionDir)nuget\*.nupkg" />
</Target>

Esto se añade justo antes del final </Project> El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer. .csproj El Comité recomienda que el Estado Parte adopte todas las medidas necesarias para garantizar la igualdad de oportunidades entre los hombres y las mujeres en materia de empleo, incluida la eliminación del trabajo forzoso.

Es dependiente de una ubicación nuget llamada 'local' que está definida en el Nuget.config archivo. que he mapped a un folder local en mi máquina.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
    <add key="Local" value="e:\nuget" />
    <add key="Microsoft Visual Studio Offline Packages" value="C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\" />
  </packageSources>
</configuration>

El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer.

En el futuro planeo hacer de esto un paquete NuGet. Utilizo esto en el blog ahora, por ejemplo para seguir cuánto tiempo tardan las traducciones

        var translationTask = tasks.FirstOrDefault(t => t.TaskId == taskId);
        if (translationTask == null) return TypedResults.BadRequest("Task not found");
        await  umamiClient.Send(new UmamiPayload(){  Name = "Get Translation"}, new UmamiEventData(){{"timetaken", translationTask.TotalMilliseconds}, {"language",translationTask.Language}});
Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.