diff --git a/README.md b/README.md index bbb852b..781b95e 100644 --- a/README.md +++ b/README.md @@ -1,231 +1,26 @@ -# PostHog DotNet Client SDK ![Build status](https://github.com/PostHog/posthog-dotnet/actions/workflows/main.yaml/badge.svg?branch=main) +# PostHog .NET ![Build status](https://github.com/PostHog/posthog-dotnet/actions/workflows/main.yaml/badge.svg?branch=main) -This repository contains a set of packages for interacting with the PostHog API in .NET applications. -This README is for those who wish to contribute to these packages. +Please see the main [PostHog docs](https://posthog.com/docs). -For documentation on the specific packages, see the README files in the respective package directories. +SDK usage examples and code snippets live in the official documentation so they stay up to date. ## Packages -| Package | Version | Description -|---------|---------| ----------- -| [PostHog.AspNetCore](src/PostHog.AspNetCore/README.md) | [![NuGet version (PostHog.AspNetCore)](https://img.shields.io/nuget/v/PostHog.AspNetCore.svg?style=flat-square)](https://www.nuget.org/packages/PostHog.AspNetCore/) | For use in ASP.NET Core projects. -| [PostHog](src/PostHog/README.md) | [![NuGet version (PostHog)](https://img.shields.io/nuget/v/PostHog.svg?style=flat-square)](https://www.nuget.org/packages/PostHog/) | The core library. Over time, this will support client environments such as Unit, Xamarin, etc. -| [PostHog.AI](src/PostHog.AI/README.md) | [![NuGet version (PostHog.AI)](https://img.shields.io/nuget/v/PostHog.AI.svg?style=flat-square)](https://www.nuget.org/packages/PostHog.AI/) | AI Observability for OpenAI and other LLM providers. +| Package | Version | Description | +|---------|---------|-------------| +| [PostHog.AspNetCore](src/PostHog.AspNetCore/README.md) | [![NuGet version (PostHog.AspNetCore)](https://img.shields.io/nuget/v/PostHog.AspNetCore.svg?style=flat-square)](https://www.nuget.org/packages/PostHog.AspNetCore/) | For use in ASP.NET Core projects. | +| [PostHog](src/PostHog/README.md) | [![NuGet version (PostHog)](https://img.shields.io/nuget/v/PostHog.svg?style=flat-square)](https://www.nuget.org/packages/PostHog/) | The core library. | +| [PostHog.AI](src/PostHog.AI/README.md) | [![NuGet version (PostHog.AI)](https://img.shields.io/nuget/v/PostHog.AI.svg?style=flat-square)](https://www.nuget.org/packages/PostHog.AI/) | AI Observability for OpenAI and other LLM providers. | -## Platform +## Documentation -The core [PostHog](./src/PostHog/README.md) package targets `netstandard2.1` and `net8.0` for broad compatibility. The [PostHog.AspNetCore](src/PostHog.AspNetCore/README.md) package targets `net8.0`. The [PostHog.AI](src/PostHog.AI/README.md) package targets `netstandard2.1` and `net8.0` for broad compatibility. +- [.NET library docs](https://posthog.com/docs/libraries/dotnet) +- [.NET error tracking docs](https://posthog.com/docs/error-tracking/installation/dotnet) ## Contributing -See [CONTRIBUTING.md](CONTRIBUTING.md) for build, sample, and test instructions. +See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup and test instructions. -## Docs +## Releasing -More detailed docs for using this library can be found at [PostHog Docs for the .NET Client SDK](https://posthog.com/docs/libraries/dotnet). - -## Installation - -For ASP.NET Core projects, install the `PostHog.AspNetCore` package: - -```bash -$ dotnet add package PostHog.AspNetCore -``` - -And register the PostHog services in `Program.cs` (or `Startup.cs`) file by calling the `AddPostHog` extension -method on `IHostApplicationBuilder` like so: - -```csharp -using PostHog; -var builder = WebApplication.CreateBuilder(args); -builder.AddPostHog(); -``` - -For other .NET projects, install the `PostHog` package: - -```bash -$ dotnet add package PostHog -``` - -And if your project supports dependency injection, register the PostHog services in `Program.cs` (or `Startup.cs`) -file by calling the `AddPostHog` extension method on `IServiceCollection`. Here's an example for a console app: - -```csharp -using PostHog; -var services = new ServiceCollection(); -services.AddPostHog(); -var serviceProvider = services.BuildServiceProvider(); -var posthog = serviceProvider.GetRequiredService(); -``` - -For a console app (or apps not using dependency injection), you can also use the `PostHogClient` directly, just make -sure it's a singleton: - -```csharp -using System; -using PostHog; - -var posthog = new PostHogClient(Environment.GetEnvironmentVariable("PostHog__PersonalApiKey")); -``` - -The `AddPostHog` methods accept an optional `Action` parameter that you can use to configure the -client. For examples, check out the [HogTied.Web sample project](../samples/HogTied.Web/Program.cs) and the unit tests. - -## Usage - -Inject the `IPostHogClient` interface into your controller or page: - -```csharp -posthog.Capture(userId, "user signed up", new() { ["plan"] = "pro" }); -``` - -```csharp -client.CapturePageView(userId, Request.Path.Value ?? "Unknown"); -``` - -### Identity - -#### Identify a user - -See the [Identifying users](https://posthog.com/docs/product-analytics/identify) for more information about identifying users. - -Identifying a user typically happens on the front-end. For example, when an authenticated user logs in, you can call `identify` to associate the user with their previously anonymous actions. - -When `identify` is called the first-time for a distinct id, PostHog will create a new user profile. If the user already exists, PostHog will update the user profile with the new data. So the typical usage of `IdentifyAsync` here will be to update the person properties that PostHog knows about your user. - -```csharp -await posthog.IdentifyAsync( - userId, - new() - { - ["email"] = "haacked@posthog.com", - ["name"] = "Phil Haack", - ["plan"] = "pro" - }); -``` - -#### Alias a user - -Use the `Alias` method to associate one identity with another. This is useful when a user logs in and you want to associate their anonymous actions with their authenticated actions. - -```csharp -await posthog.AliasAsync(sessionId, userId); -``` - -### Analytics - -#### Capture an Event - -Note that capturing events is designed to be fast and done in the background. You can configure how often batches are sent to the PostHog API using the `FlushAt` and `FlushInterval` settings. - -```csharp -posthog.Capture(userId, "user signed up", new() { ["plan"] = "pro" }); -``` - -#### Capture a Page View - -```csharp -posthog.CapturePageView(userId, Request.Path.Value ?? "Unknown"); -``` - -#### Capture a Screen View - -```csharp -posthog.CaptureScreen(userId, "Main Screen"); -``` - -### Feature Flags - -#### Check if feature flag is enabled - -Check if the `awesome-new-feature` feature flag is enabled for the user with the id `userId`. - -```csharp -var enabled = await posthog.IsFeatureEnabledAsync(userId, "awesome-new-feature"); -``` - -You can override properties of the user stored on PostHog servers for the purposes of feature flag evaluation. -For example, suppose you offer a temporary pro-plan for the duration of the user's session. You might do this: - -```csharp -if (await posthog.IsFeatureEnabledAsync( - "pro-feature", - "some-user-id", - personProperties: new() { ["plan"] = "pro" })) -{ - // Access to pro feature -} -``` - -If you have group analytics enabled, you can also override group properties. - -```csharp -if (await posthog.IsFeatureEnabledAsync( - "large-project-feature", - "some-user-id", - new FeatureFlagOptions - { - Groups = [new Group(groupType: "project", groupKey: "project-group-key") { ["size"] = "large" }] - })) -{ - // Access large project feature -} -``` - -> [!NOTE] -> Specifying `PersonProperties` and `GroupProperties` is necessary when using local evaluation of feature flags. - -#### Evaluation contexts - -You can configure SDK-level feature flag evaluation contexts to only evaluate flags intended for this application, -platform, or product area. - -```csharp -services.AddPostHog(options => -{ - options.ProjectToken = "YOUR_PROJECT_TOKEN"; - options.EvaluationContexts = ["main-app", "api", "backend"]; -}); -``` - -Flags without evaluation contexts continue to evaluate for all SDKs. Flags with contexts evaluate only when at least -one configured context matches. - -### Get a single Feature Flag - -Some feature flags may have associated payloads. - -```csharp -if (await posthog.GetFeatureFlagAsync("awesome-new-feature", "some-user-id") is { Payload: {} payload }) -{ - // Do something with the payload. - Console.WriteLine($"The payload is: {payload}"); -} -``` - -### Get All Feature Flags - -Using information on the PostHog server. - -```csharp -var flags = await posthog.GetAllFeatureFlagsAsync("some-user-id"); -``` - -Overriding the group properties for the current user. - -```csharp -var flags = await posthog.GetAllFeatureFlagsAsync( -"some-user-id", -options: new AllFeatureFlagsOptions -{ - Groups = - [ - new Group("project", "aaaa-bbbb-cccc") - { - ["$group_key"] = "aaaa-bbbb-cccc", - ["size"] = "large" - } - ] -}); -``` +See [RELEASING.md](RELEASING.md). diff --git a/src/PostHog.AI/README.md b/src/PostHog.AI/README.md index 474c1e4..00164cc 100644 --- a/src/PostHog.AI/README.md +++ b/src/PostHog.AI/README.md @@ -1,249 +1,13 @@ -# PostHog.AI +# PostHog.AI for .NET -This package provides AI Observability for .NET applications using PostHog. It currently supports intercepting and tracing requests to OpenAI and Azure OpenAI. +> [!WARNING] +> `PostHog.AI` is currently pre-release. We're making it available publicly to solicit feedback, but there will be breaking changes until it reaches a stable release. -> [!WARNING] -> This package is currently in a pre-release stage. We're making it available publicly to solicit -> feedback. While we always strive to maintain a high level of quality, use this package at your own -> risk. There *will* be many breaking changes until we reach a stable release. +Please see the main [PostHog docs](https://posthog.com/docs). -## Installation +SDK usage examples and code snippets live in the official documentation so they stay up to date. -Install the `PostHog.AI` package via NuGet: +## Documentation -```bash -dotnet add package PostHog.AI -``` - -## Usage - -### Dependency Injection (Recommended) - -The easiest way to use `PostHog.AI` is via the `AddPostHogOpenAIClient` extension method. This registers an `OpenAIClient` that is automatically configured to use PostHog for observability. - -```csharp -using PostHog; -using PostHog.AI; -using OpenAI; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -var builder = Host.CreateApplicationBuilder(args); - -// 1. Configure PostHog -builder.Services.AddPostHog(options => -{ - options.ProjectToken = "YOUR_PROJECT_TOKEN"; - options.HostUrl = new Uri("https://us.i.posthog.com"); // or eu.i.posthog.com -}); - -// 2. Register OpenAI Client with PostHog integration -// This registers OpenAIClient as a Singleton and configures it to use the PostHog interceptor. -// You can also chain additional HTTP handlers here (e.g., for resilience). -builder.Services.AddPostHogOpenAIClient("YOUR_OPENAI_API_KEY", options => -{ - // Optional: Configure OpenAIClientOptions here -}); - -var host = builder.Build(); - -// 3. Inject and use OpenAIClient -var openAIClient = host.Services.GetRequiredService(); -// ... use client -``` - -> **Note:** `AddPostHogOpenAIClient` overrides the `Transport` property of `OpenAIClientOptions` to inject the PostHog handler. If you need a custom Transport implementation (rare), you should manually configure the client as shown below. - -### Manual Configuration (Advanced) - -If you need more control or are not using the helper method: - -```csharp -using PostHog.AI; -using OpenAI; -using System.ClientModel; -using System.ClientModel.Primitives; - -// 1. Register PostHog AI services -builder.Services.AddPostHogAI(); - -// 2. Configure OpenAI Client with the intercepting handler manually -builder.Services.AddHttpClient("PostHogOpenAI") // Named client - .AddPostHogOpenAIHandler(); - -builder.Services.AddSingleton(sp => -{ - var httpClientFactory = sp.GetRequiredService(); - var httpClient = httpClientFactory.CreateClient("PostHogOpenAI"); - - // Explicitly set the transport - var options = new OpenAIClientOptions - { - Transport = new HttpClientPipelineTransport(httpClient) - }; - - return new OpenAIClient(new ApiKeyCredential("YOUR_OPENAI_API_KEY"), options); -}); -``` - -## Supported Features - -- **Trace Capture**: Automatically captures `$ai_generation` and `$ai_embedding` events. -- **Token Usage**: Captures prompt, completion, and total token usage. -- **Model Parameters**: Tracks model configuration including temperature, max_tokens, stream, and tools. -- **Latency**: Measures and records request latency in seconds. -- **Streaming**: Supports capturing content from streamed responses (Server-Sent Events). -- **Embeddings**: Automatically detects and captures `$ai_embedding` events for embedding requests. -- **Error Handling**: Captures error messages and HTTP status codes. -- **Span and Session Tracking**: Supports grouping events with session IDs, span IDs, and parent-child relationships via `PostHogAIContext`. -- **Cache Tracking**: Captures cache token usage when available in API responses. - -## Captured Event Properties - -The handler automatically extracts and captures the following properties from OpenAI API requests and responses: - -### Core Properties - -- `$ai_trace_id` - Unique identifier (UUID) for grouping AI events -- `$ai_provider` - AI service provider (e.g., "openai") -- `$ai_lib` - Library identifier ("posthog-dotnet") -- `$ai_model` - Model name used for the generation -- `$ai_latency` - Request latency in seconds -- `$ai_base_url` - Base URL of the API endpoint -- `$ai_request_url` - Full URL of the API request -- `$ai_http_status` - HTTP status code of the response - -### Model Parameters - -- `$ai_temperature` - Temperature parameter used in the request -- `$ai_max_tokens` - Maximum tokens setting -- `$ai_stream` - Whether the response was streamed -- `$ai_tools` - Tools/functions available to the model -- `$ai_model_parameters` - Dictionary of other model parameters (top_p, frequency_penalty, presence_penalty, stop, etc.) - -### Input/Output - -- `$ai_input` - Input messages, prompt, or input data -- `$ai_output_choices` - Response choices from the LLM (for generation events) -- `$ai_input_tokens` - Number of tokens in the input -- `$ai_output_tokens` - Number of tokens in the output -- `$ai_total_tokens` - Total number of tokens used - -### Context-Based Properties - -These properties can be set via `PostHogAIContext` (see below): - -- `$ai_session_id` - Groups related traces together -- `$ai_span_id` - Unique identifier for this generation -- `$ai_span_name` - Name given to this generation -- `$ai_parent_id` - Parent span ID for tree view grouping - -### Cache Properties - -- `$ai_cache_read_input_tokens` - Number of tokens read from cache (when available) -- `$ai_cache_creation_input_tokens` - Number of tokens written to cache (when available) - -### Error Properties - -- `$ai_is_error` - Boolean indicating if the request was an error -- `$ai_error` - Error message or object - -## Using PostHogAIContext - -`PostHogAIContext` allows you to set additional context for AI events within a scope, including session tracking, span tracking, and custom properties. - -### Basic Usage - -```csharp -using PostHog.AI; - -// Begin a scope with context -using (PostHogAIContext.BeginScope( - distinctId: "user-123", - traceId: "trace-abc", - sessionId: "session-xyz", - spanId: "span-1", - spanName: "summarize_text", - parentId: null)) -{ - // All OpenAI API calls within this scope will include the context - var chatClient = openAIClient.GetChatClient("gpt-4"); - var response = await chatClient.CompleteChatAsync("Summarize this text"); -} -// Context is automatically restored when the scope ends -``` - -### Span Tracking Example - -```csharp -using PostHog.AI; - -// Parent span -using (PostHogAIContext.BeginScope( - traceId: "trace-123", - spanId: "span-parent", - spanName: "process_document")) -{ - // Child span 1 - using (PostHogAIContext.BeginScope( - traceId: "trace-123", // Same trace - spanId: "span-child-1", - spanName: "extract_summary", - parentId: "span-parent")) // Link to parent - { - // First AI call - var chatClient = openAIClient.GetChatClient("gpt-4"); - await chatClient.CompleteChatAsync("Summarize the document"); - } - - // Child span 2 - using (PostHogAIContext.BeginScope( - traceId: "trace-123", - spanId: "span-child-2", - spanName: "extract_keywords", - parentId: "span-parent")) - { - // Second AI call - var chatClient = openAIClient.GetChatClient("gpt-4"); - await chatClient.CompleteChatAsync("Extract keywords from the document"); - } -} -``` - -### Custom Properties - -You can add custom properties that will be merged into the event properties: - -```csharp -using PostHog.AI; - -var customProperties = new Dictionary -{ - { "workflow_id", "workflow-123" }, - { "feature_flag", "new-model-v2" }, - { "$ai_session_id", "override-session" } // Can override context properties -}; - -using (PostHogAIContext.BeginScope( - sessionId: "default-session", - properties: customProperties)) -{ - // Properties dictionary takes precedence over context properties - // In this case, $ai_session_id will be "override-session" - var chatClient = openAIClient.GetChatClient("gpt-4"); - await chatClient.CompleteChatAsync("Hello"); -} -``` - -### Property Precedence - -Properties are applied in the following order (later values override earlier ones): - -1. Context properties (`SessionId`, `SpanId`, etc.) -2. Properties dictionary (can override context properties) - -This allows you to set default context properties via `BeginScope` and override them for specific events via the Properties dictionary. - -## Configuration - -The handler uses the configured `IPostHogClient` to send events. Ensure your PostHog client is correctly configured with your API key and host URL. +- [.NET AI observability docs](https://posthog.com/docs/libraries/dotnet#ai-observability) +- [AI observability docs](https://posthog.com/docs/ai-observability) diff --git a/src/PostHog.AspNetCore/README.md b/src/PostHog.AspNetCore/README.md index dedaff1..989a459 100644 --- a/src/PostHog.AspNetCore/README.md +++ b/src/PostHog.AspNetCore/README.md @@ -1,350 +1,9 @@ -# PostHog.AspNetCore +# PostHog ASP.NET Core SDK -This is a client SDK for the PostHog API written in C#. This package depends on the PostHog package and provides -additional functionality for ASP.NET Core projects. +Please see the main [PostHog docs](https://posthog.com/docs). -## Installation +SDK usage examples and code snippets live in the official documentation so they stay up to date. -Use the `dotnet` CLI to add the package to your project: +## Documentation -```bash -$ dotnet add package PostHog.AspNetCore -``` - -## Configuration - -Register your PostHog instance in `Program.cs` (or `Startup.cs` depending on how you roll): - -```csharp -var builder = WebApplication.CreateBuilder(args); -builder.AddPostHog(); -``` - -Set your project token using user secrets: - -```bash -$ dotnet user-secrets set PostHog:ProjectToken YOUR_PROJECT_TOKEN -``` - -In most cases, that's all you need to configure! - -If you're using the EU hosted instance of PostHog or a self-hosted instance, you can configure the `HostUrl` setting in -`appsettings.json`: - -```json -{ - ... - "PostHog": { - "HostUrl": "https://eu.i.posthog.com" - } -} -``` - -There are some more settings you can configure if you want to tweak the behavior of the client, but the defaults -should work in most cases. - -The available options are: - -| Option | Description | Default | -|-----------------|-------------------------------------------------------------------|--------------------------| -| `HostUrl` | The URL of the PostHog instance. | https://us.i.posthog.com | -| `MaxBatchSize` | The maximum number of events to send in a single batch. | `100` | -| `MaxQueueSize` | The maximum number of events to store in the queue at any time. | `1000` | -| `FlushAt` | The number of events to enqueue before sending events to PostHog. | `20` | -| `FlushInterval` | The interval in milliseconds between periodic flushes. | `30` seconds | - -> [!NOTE] -> The client will attempt to send events to PostHog in the background. It sends it every `FlushInterval` or when -> `FlushAt` events have been enqueued. However, if the network is down or if there's a spike in events, the queue -> could grow without restriction. The `MaxQueueSize` setting is there to prevent the queue from growing too large. -> When that number is reached, the client will start dropping older events. `MaxBatchSize` ensures that the `/batch` -> request doesn't get too large. - -### Local Evaluation - -If you want to evaluate feature flags locally, you'll need to provide a personal API key for the PostHog API. - -For local development, you can set the `PostHog:PersonalApiKey` setting in your user secrets: - -```bash -$ dotnet user-secrets set PostHog:PersonalApiKey YOUR_PERSONAL_API_KEY -``` - -For production, we recommend using a secrets manager or environment variables to set the `PostHog:PersonalApiKey` setting. - -## Docs - -More detailed docs for using this library can be found at [PostHog Docs for the .NET Client SDK](https://posthog.com/docs/libraries/dotnet). - -## Usage - -### Frontend-to-backend request context - -If your frontend sends PostHog JS request context headers, import the ASP.NET Core helpers and add the middleware before routes that call PostHog: - -```csharp -using PostHog; -using PostHog.AspNetCore; - -app.UsePostHogRequestContext(); -``` - -This reads client-controlled `X-POSTHOG-DISTINCT-ID` and `X-POSTHOG-SESSION-ID` headers into a request-local analytics context. Installing the middleware opts the pipeline into request context extraction; individual PostHog calls use request-context identity only when they omit an explicit `distinctId` or call a parameterless request-context helper. Explicit distinct IDs always override request context. Captures inside the request can then omit `distinctId`: - -```csharp -using PostHog.AspNetCore; - -posthog.Capture("checkout started"); -``` - -These headers are client-controlled analytics context, not authentication. If `X-POSTHOG-DISTINCT-ID` is missing, normal captures become personless by default. - -The request-context `EvaluateFlagsAsync()` overloads also use the current request context distinct ID. Use them for analytics-driven consistency with frontend flag evaluation, not authorization checks. For security-sensitive server branching, pass an authenticated distinct ID explicitly: - -```csharp -var flags = await posthog.EvaluateFlagsAsync(authenticatedUserId); -``` - -You can disable tracing header use while still collecting request metadata: - -```csharp -app.UsePostHogRequestContext(options => -{ - options.UseTracingHeaders = false; -}); -``` - -The middleware also adds request metadata such as `$current_url`, `$request_method`, `$request_path`, `$user_agent`, and `$ip`. `$current_url` omits the query string to avoid sending secrets such as OAuth codes, reset tokens, or signed URL parameters. - -If your app is behind a proxy, configure ASP.NET Core forwarded headers before this middleware so `$ip` uses the normalized `HttpContext.Connection.RemoteIpAddress` value. - -Unhandled downstream exception capture is disabled by default to avoid duplicate reporting if your app already captures exceptions elsewhere. Enable it explicitly to capture exceptions with the active request context and rethrow them. Exception capture uses the same request context identity/session/properties as regular captures: - -```csharp -app.UsePostHogRequestContext(options => -{ - options.CaptureExceptions = true; -}); -``` - -Inject the `IPostHogClient` interface into your controller or page: - -```csharp -public class HomeController(IPostHogClient posthog) : Controller -{ - public IActionResult SignUpComplete() - { - var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; - posthog.Capture(userId, "user signed up", new() { ["plan"] = "pro" }); - return View(); - } -} -``` - -```csharp -public class IndexModel(IPostHogClient posthog) : PageModel -{ - public void OnGet() - { - var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; - posthog.CapturePageView(userId, Request.Path.Value ?? "Unknown"); - } -} -``` - -### Identity - -#### Identify a user - -See the [Identifying users](https://posthog.com/docs/product-analytics/identify) for more information about identifying users. - -Identifying a user typically happens on the front-end. For example, when an authenticated user logs in, you can call `identify` to associate the user with their previously anonymous actions. - -When `identify` is called the first-time for a distinct id, PostHog will create a new user profile. If the user already exists, PostHog will update the user profile with the new data. So the typical usage of `IdentifyAsync` here will be to update the person properties that PostHog knows about your user. - -```csharp -await posthog.IdentifyAsync( - userId, - new() - { - ["email"] = "haacked@posthog.com", - ["name"] = "Phil Haack", - ["plan"] = "pro" - }); -``` - -#### Alias a user - -Use the `Alias` method to associate one identity with another. This is useful when a user logs in and you want to associate their anonymous actions with their authenticated actions. - -```csharp -var sessionId = Request.Cookies["session_id"]; // Used for anonymous actions. -var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; // Now we know who they are. -await posthog.AliasAsync(sessionId, userId); -``` - -### Analytics - -#### Capture an Event - -Note that capturing events is designed to be fast and done in the background. You can configure how often batches are sent to the PostHog API using the `FlushAt` and `FlushInterval` settings. - -```csharp -posthog.Capture(userId, "user signed up", new() { ["plan"] = "pro" }); -``` - -#### Capture a Page View - -```csharp -posthog.CapturePageView(userId, Request.Path.Value ?? "Unknown"); -``` - -#### Capture a Screen View - -```csharp -posthog.CaptureScreen(userId, "Main Screen"); -``` - -### Feature Flags - -#### Check if feature flag is enabled - -Check if the `awesome-new-feature` feature flag is enabled for the user with the id `userId`. - -```csharp -var enabled = await posthog.IsFeatureEnabledAsync(userId, "awesome-new-feature"); -``` - -### Get a single Feature Flag - -Some feature flags may have associated payloads. - -```csharp -if (await posthog.GetFeatureFlagAsync("awesome-new-feature", "some-user-id") is { Payload: {} payload }) -{ - // Do something with the payload. - Console.WriteLine($"The payload is: {payload}"); -} -``` - -### Get All Feature Flags - -Using information on the PostHog server. - -```csharp -var flags = await posthog.GetAllFeatureFlagsAsync("some-user-id"); -``` - -Overriding the group properties for the current user. - -```csharp -var flags = await posthog.GetAllFeatureFlagsAsync( -"some-user-id", -options: new AllFeatureFlagsOptions -{ - Groups = - [ - new Group("project", "aaaa-bbbb-cccc") - { - ["$group_key"] = "aaaa-bbbb-cccc", - ["size"] = "large" - } - ] -}); -``` - -### Evaluation contexts - -Configure SDK-level feature flag evaluation contexts to only evaluate flags intended for this application, -platform, or product area. - -```csharp -builder.Services.AddPostHog(options => -{ - options.ProjectToken = "YOUR_PROJECT_TOKEN"; - options.EvaluationContexts = ["main-app", "api", "backend"]; -}); -``` - -Flags without evaluation contexts continue to evaluate for all SDKs. Flags with contexts evaluate only when at least -one configured context matches. - -## Feature Management - -`PostHog.AspNetCore` supports .NET Feature Management. This allows you to use the <feature /> tag helper and -the `FeatureGateAttribute` in your ASP.NET Core applications to gate access to certain features using PostHog -feature flags. - -### Setup - -To use feature flags with the .NET Feature Management library, you'll need to implement the -`IPostHogFeatureFlagContextProvider` interface. The quickest way to do that is to inherit from the -`PostHogFeatureFlagContextProvider` class and override the `GetDistinctId` and `GetFeatureFlagOptionsAsync` methods. - -```csharp -public class MyFeatureFlagContextProvider(IHttpContextAccessor httpContextAccessor) - : PostHogFeatureFlagContextProvider -{ - protected override string? GetDistinctId() => httpContextAccessor.HttpContext?.User.Identity?.Name; - - protected override ValueTask GetFeatureFlagOptionsAsync() - { - // In a real app, you might get this information from a database or other source for the current user. - return ValueTask.FromResult( - new FeatureFlagOptions - { - PersonProperties = new Dictionary - { - ["email"] = "some-test@example.com" - }, - OnlyEvaluateLocally = true - }); - } -} -``` - -Then, register your implementation in `Program.cs` (or `Startup.cs`): - -```csharp -var builder = WebApplication.CreateBuilder(args); -builder.AddPostHog(options => { - options.UseFeatureManagement(); -}); -``` - -### Usage - -You can now use `feature` tag helpers in your Razor views: - -```html - -

This is the new feature!

-
- -

Sorry, no awesome new feature for you.

-
-``` - -Multivariate feature flags are also supported: - -```html - -

This is the new feature variant A!

-
- -

This is the new feature variant B!

-
-``` - -You can also use the `FeatureGateAttribute` to gate access to controllers or actions: - -```csharp -[FeatureGate("awesome-new-feature")] -public class NewFeatureController : Controller -{ - public IActionResult Index() - { - return View(); - } -} -``` +- [.NET library docs](https://posthog.com/docs/libraries/dotnet) diff --git a/src/PostHog/README.md b/src/PostHog/README.md index 61e5402..7c18175 100644 --- a/src/PostHog/README.md +++ b/src/PostHog/README.md @@ -1,61 +1,9 @@ # PostHog .NET SDK -This is a client SDK for the PostHog API written in C#. This is the core implementation of PostHog. +Please see the main [PostHog docs](https://posthog.com/docs). -## Goals +SDK usage examples and code snippets live in the official documentation so they stay up to date. -The goal of this package is to be usable in multiple .NET environments. At this moment, we are far short of that goal. We only support ASP.NET Core via [PostHog.AspNetCore](../PostHog.AspNetCore/README.md). +## Documentation -## Docs - -More detailed docs for using this library can be found at [PostHog Docs for the .NET Client SDK](https://posthog.com/docs/libraries/dotnet). - -## Usage - -To use this package, create an instance of `PostHogClient` and call the appropriate methods. Here's an example: - -```csharp -using PostHog; - -var client = new PostHogClient(new PostHogOptions { ProjectToken = "YOUR_PROJECT_TOKEN" }); -client.Capture("user-123", "Test Event"); -``` - -For console apps, scripts, or other places where passing a client instance around is inconvenient, you can configure a process-wide default client and use the `PostHogSdk` facade: - -```csharp -using PostHog; -using PostHog.Sdk; - -PostHogSdk.Init(new PostHogOptions { ProjectToken = "YOUR_PROJECT_TOKEN" }); -PostHogSdk.Capture("user-123", "Test Event"); - -await PostHogSdk.ShutdownAsync(); -``` - -You can also assign an existing client: - -```csharp -using PostHog; -using PostHog.Sdk; - -var client = new PostHogClient(new PostHogOptions { ProjectToken = "YOUR_PROJECT_TOKEN" }); -PostHogSdk.DefaultClient = client; - -PostHogSdk.Capture("user-123", "Test Event"); -``` - -If no default client is configured, `PostHogSdk` methods are no-ops and log a warning once through `Microsoft.Extensions.Logging`. To enable SDK logs for the static facade and clients created by `PostHogSdk.Init`, set `PostHogSdk.LoggerFactory` before calling SDK methods: - -```csharp -using Microsoft.Extensions.Logging; -using PostHog.Sdk; - -using var loggerFactory = LoggerFactory.Create(builder => -{ - builder.AddConsole(); - builder.SetMinimumLevel(LogLevel.Information); -}); - -PostHogSdk.LoggerFactory = loggerFactory; -``` +- [.NET library docs](https://posthog.com/docs/libraries/dotnet)