How we take data-driven decisions with LogSnag
Tags: typescript, analytics, tracking, nextjs
Published: 2024-04-16 15:50:40


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ln61zi7jqytcnr5es51g.png)

It's important for every new product to take the right decisions as often you can, and thanks for our public roadmap, votes on feature request and building in public we have a deep connection with our users, but on top of that we also want data to back our decisions. And thats why we have implemented LogSnag to track a lot of events so we can take data-driven decisions too.


There are planty of ways how you can implement analytics, in this blog post we will share how we solved it in [Midday](https://midday.ai) using NextJS and server-actions.


Because we have a monorepo we started with creating a new package called `@midday/events` where we install `@logsnag/next`.


The package includes all the events we want to track in Midday for example:

```
{
    SignedIn: {
        name: "User Signed In",
        icon: "🌝",
        channel: "login",
    },
        SignOut: {
        name: "User Signed Out",
        icon: "🌝",
        channel: "login",
    },
}
```

And based of these realtime events we can make clear graphs like Line Chart, Bar Chart and Funnel Charts.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ymsige3uwbfhpwt7koix.png)

### How we implemented LogSnag
When you sign in to [Midday](https://midday.ai) we first ask you about tracking, we want you to keep your privacy. 

This is done by showing a `Toast` component with the option to Accept or Decline, we save this decision in a cookie so we now if we should add a identifier for the events or not.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apdo4j0q64piasq2u79a.png)

We run a server action called `tracking-consent-action.ts`:

```
"use server";

import { Cookies } from "@/utils/constants";
import { addYears } from "date-fns";
import { cookies } from "next/headers";
import { action } from "./safe-action";
import { trackingConsentSchema } from "./schema";

export const trackingConsentAction = action(
  trackingConsentSchema,
  async (value) => {
    cookies().set({
      name: Cookies.TrackingConsent,
      value: value ? "1" : "0",
      expires: addYears(new Date(), 1),
    });

    return value;
  }
);
```

We then wrap the `track` method from LogSnag to enable or disabled the `user_id` to the event.

```
export const setupLogSnag = async (options?: Props) => {
  const { userId, fullName } = options ?? {};
  const consent = cookies().get(Cookies.TrackingConsent)?.value === "0";

  const logsnag = new LogSnag({
    token: process.env.LOGSNAG_PRIVATE_TOKEN!,
    project: process.env.NEXT_PUBLIC_LOGSNAG_PROJECT!,
    disableTracking: Boolean(process.env.NEXT_PUBLIC_LOGSNAG_DISABLED!),
  });

  if (consent && userId && fullName) {
    await logsnag.identify({
      user_id: userId,
      properties: {
        name: fullName,
      },
    });
  }

  return {
    ...logsnag,
    track: (options: TrackOptions) =>
      logsnag.track({
        ...options,
        user_id: consent ? userId : undefined,
      }),
  };
};
```

We use the `setupLogSnag` function like this:

```
export const exportTransactionsAction = action(
  exportTransactionsSchema,
  async (transactionIds) => {
    const user = await getUser();

    const event = await client.sendEvent({
      name: Events.TRANSACTIONS_EXPORT,
      payload: {
        transactionIds,
        teamId: user.data.team_id,
        locale: user.data.locale,
      },
    });

    const logsnag = await setupLogSnag({
      userId: user.data.id,
      fullName: user.data.full_name,
    });

    logsnag.track({
      event: LogEvents.ExportTransactions.name,
      icon: LogEvents.ExportTransactions.icon,
      channel: LogEvents.ExportTransactions.channel,
    });

    return event;
  }
);
```

We have a lot of events and charts pushing us to take right decisions, you can find the source code for this in our repository [here](https://git.new/midday).