> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Add context on your Blackfire timeline from a Google Alerts RSS Feed

export const PostMeta = ({data = {}}) => {
  const {author, date, image} = data;
  const authors = Array.isArray(author) ? author : author ? [author] : [];
  const resolveAuthor = slug => {
    const entry = AUTHOR_MAP[slug] || ({});
    const name = entry.name || slug;
    const github = entry.github || null;
    const linkedin = entry.linkedin || null;
    const url = github ? `https://github.com/${github}` : linkedin || null;
    const avatarUrl = github ? `https://github.com/${github}.png?size=64` : null;
    return {
      name,
      url,
      avatarUrl
    };
  };
  const formattedDate = date ? new Date(date).toLocaleDateString('en-US', {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }) : null;
  if (!image && authors.length === 0 && !formattedDate) return null;
  const AUTHOR_MAP = {
    "aaron-collier": {
      "name": "Aaron Collier"
    },
    "aaron-dudenhofer": {
      "name": "Aaron Dudenhofer"
    },
    "aaron-porter": {
      "name": "Aaron Porter"
    },
    "adriaan-odendaal": {
      "name": "Adriaan Odendaal"
    },
    "ajmal": {
      "name": "Ajmal Siddiqui"
    },
    "akalipetis": {
      "name": "Antonis Kalipetis"
    },
    "alexander-varwijk": {
      "name": "Alexander Varwijk"
    },
    "alicia-bevilacqua": {
      "name": "Alicia Bevilacqua"
    },
    "amelie-deguerry": {
      "name": "Amelie Deguerry"
    },
    "anacidre": {
      "name": "Ana Cidre",
      "linkedin": "https://www.linkedin.com/in/ana-cidre"
    },
    "andoni": {
      "name": "Andoni Auzmendi"
    },
    "andrei-taranu": {
      "name": "Andrei (Alex) Taranu",
      "linkedin": "https://www.linkedin.com/in/andrei-alex-taranu/"
    },
    "andrew-baxter": {
      "name": "Andrew Baxter"
    },
    "andrew-melck": {
      "name": "Andrew Melck"
    },
    "antoine-crochet-damais": {
      "name": "Antoine Crochet Damais"
    },
    "augustin-delaporte": {
      "name": "Augustin Delaporte",
      "linkedin": "https://www.linkedin.com/in/augustindelaporte/"
    },
    "branislav-bujisic": {
      "name": "Branislav Bujisic"
    },
    "carl-smith": {
      "name": "Carl Smith"
    },
    "caroline-leroy": {
      "name": "Caroline Leroy"
    },
    "cati-mayer": {
      "name": "Cati Mayer"
    },
    "catplat": {
      "name": "C Trinkwon"
    },
    "ceelolulu": {
      "name": "Celeste van der Watt"
    },
    "chadwcarlson": {
      "name": "Chad Carlson",
      "github": "chadwcarlson",
      "linkedin": "https://www.linkedin.com/in/chadwcarlson"
    },
    "chris-ward": {
      "name": "Chris Ward"
    },
    "chris-yates": {
      "name": "Chris Yates"
    },
    "christian-sieber": {
      "name": "Christian Sieber"
    },
    "christopher-lockheardt": {
      "name": "Christopher Lockheardt"
    },
    "christopher-skene": {
      "name": "Christopher Skene"
    },
    "chuck-morgan": {
      "name": "Chuck Morgan"
    },
    "corey-dockendorf": {
      "name": "Corey Dockendorf"
    },
    "crell": {
      "name": "Crell"
    },
    "damz": {
      "name": "Damz"
    },
    "dan-morrison": {
      "name": "Dan Morrison"
    },
    "davidbonachera": {
      "name": "David Bonachera",
      "github": "davidbonachera",
      "linkedin": "https://www.linkedin.com/in/davidbonachera"
    },
    "dereliahmet1": {
      "name": "Ahmet Faruk Dereli"
    },
    "devicezero": {
      "name": "Jonas Kröger",
      "github": "devicezero",
      "linkedin": "https://www.linkedin.com/in/jonaskroeger/"
    },
    "doug-goldberg": {
      "name": "Doug Goldberg"
    },
    "duncan-naves": {
      "name": "Duncan Naves",
      "github": "duncannaves",
      "linkedin": "https://www.linkedin.com/in/duncan-naves-a94423aa"
    },
    "erika-bustamante": {
      "name": "Erika Bustamante"
    },
    "fabpot": {
      "name": "Fabien Potencier"
    },
    "flovntp": {
      "name": "Florent Huck",
      "github": "flovntp",
      "linkedin": "https://www.linkedin.com/in/florenthuck"
    },
    "fred-plais": {
      "name": "Fred Plais"
    },
    "gauthier-garnier": {
      "name": "Gauthier Garnier"
    },
    "gilzow": {
      "name": "Paul Gilzow"
    },
    "gmoigneu": {
      "name": "Guillaume Moigneu",
      "github": "gmoigneu",
      "linkedin": "https://www.linkedin.com/in/guillaumemoigneu/"
    },
    "gregqualls": {
      "name": "Greg Qualls"
    },
    "guguss": {
      "name": "Augustin Delaporte"
    },
    "haylee-millar": {
      "name": "Haylee Millar"
    },
    "ivana-kotur": {
      "name": "Ivana Kotur"
    },
    "jackrabbithanna": {
      "name": "Mark Hanna"
    },
    "jared-wright": {
      "name": "Jared Wright",
      "github": "jww-sh",
      "linkedin": "https://www.linkedin.com/in/jaredwaynewright"
    },
    "jessica-orozco": {
      "name": "Jessica Orozco"
    },
    "joey-stanford": {
      "name": "Joey Stanford"
    },
    "john-grubb": {
      "name": "John Grubb"
    },
    "jonas-kruger": {
      "name": "Jonas Kruger"
    },
    "kathryn-frazer": {
      "name": "Kathryn Frazer"
    },
    "kemiojo": {
      "name": "Kemi Elizabeth Ojogbede"
    },
    "kieronsambrook-smith": {
      "name": "Kieronsambrook Smith"
    },
    "laurent-arnoud": {
      "name": "Laurent Arnoud",
      "linkedin": "https://www.linkedin.com/in/laurent-arnoud-861b44121/"
    },
    "letoya-boyne": {
      "name": "Letoya Boyne"
    },
    "lolautruche": {
      "name": "Jérôme Vieilledent"
    },
    "lyly-lepinay": {
      "name": "Lyly Lepinay"
    },
    "manauwar-alam": {
      "name": "Manauwar Alam"
    },
    "marc-antoine-porri": {
      "name": "Marc Antoine Porri"
    },
    "maria-antinkaapo": {
      "name": "Maria Antinkaapo"
    },
    "maria-de-anton": {
      "name": "Maria De Anton"
    },
    "mark-dorison": {
      "name": "Mark Dorison"
    },
    "markus-hausammann": {
      "name": "Markus Hausammann"
    },
    "mary-thomas": {
      "name": "Mary Thomas"
    },
    "mathias-bolt-lesniak": {
      "name": "Mathias Bolt Lesniak"
    },
    "mathieu-strauch": {
      "name": "Mathieu Strauch"
    },
    "matthias-van-woensel": {
      "name": "Matthias Van Woensel",
      "linkedin": "https://www.linkedin.com/in/matthias-van-woensel-267a069"
    },
    "michael-sharp": {
      "name": "Michael Sharp"
    },
    "mupsi": {
      "name": "Marine Gandy"
    },
    "natalie-harper": {
      "name": "Natalie Harper"
    },
    "ngommenginger": {
      "name": "Nicolas Gommenginger",
      "linkedin": "https://www.linkedin.com/in/nicolas-gommenginger"
    },
    "nicholas-bennison": {
      "name": "Nicholas Bennison"
    },
    "nicholas-vahalik": {
      "name": "Nicholas Vahalik"
    },
    "nick-hardiman": {
      "name": "Nick Hardiman"
    },
    "nickanderegg": {
      "name": "Nickanderegg"
    },
    "nicolas-grekas": {
      "name": "Nicolas Grekas",
      "github": "nicolas-grekas",
      "linkedin": "https://www.linkedin.com/in/nicolasgrekas/"
    },
    "niti-malwade": {
      "name": "Niti Malwade"
    },
    "opensocialteam": {
      "name": "Opensocialteam"
    },
    "ori-pekelman": {
      "name": "Ori Pekelman"
    },
    "otavio-santana": {
      "name": "Otavio Santana"
    },
    "palwandi": {
      "name": "Pawan Alwandi",
      "github": "pawpy",
      "linkedin": "https://www.linkedin.com/in/pawanalwandi"
    },
    "patrick-boest": {
      "name": "Patrick Boest"
    },
    "patrick-dawkins": {
      "name": "Patrick Dawkins",
      "github": "pjcdawkins",
      "linkedin": "https://www.linkedin.com/in/patrickdawkins"
    },
    "patrick-klima": {
      "name": "Patrick Klima"
    },
    "pjcdawkins": {
      "name": "Pjcdawkins"
    },
    "prineet-kaurbhurji": {
      "name": "Prineet Kaurbhurji"
    },
    "quentin-sinig": {
      "name": "Quentin Sinig"
    },
    "ralt": {
      "name": "Florian Margaine",
      "github": "ralt",
      "linkedin": "https://www.linkedin.com/in/florian-margaine-43971136"
    },
    "ramanathanramakrishnamurthy": {
      "name": "Ramanathanramakrishnamurthy"
    },
    "remi-lejeune": {
      "name": "Rémi Lejeune"
    },
    "ribel": {
      "name": "Taras Kruts"
    },
    "robert-douglass": {
      "name": "Robert Douglass"
    },
    "rudy-weber": {
      "name": "Rudy Weber"
    },
    "ryan-hicks": {
      "name": "Ryan Hicks"
    },
    "sabri-helal": {
      "name": "Sabri Helal"
    },
    "savannah-bergeron": {
      "name": "Savannah Bergeron"
    },
    "shannon-vettes": {
      "name": "Shannon Vettes"
    },
    "shawn-ogasawara": {
      "name": "Shawn Ogasawara",
      "linkedin": "https://www.linkedin.com/in/shawn-ogasawara-83a9a0/"
    },
    "shawna-spoor": {
      "name": "Shawna Spoor"
    },
    "shedrack-akintayo": {
      "name": "Shedrack Akintayo"
    },
    "simon-ruggier": {
      "name": "Simon Ruggier"
    },
    "sophie-van-der-kindere": {
      "name": "Sophie Van Der Kindere"
    },
    "stefanos-thampis": {
      "name": "Stefanos Thampis"
    },
    "stephen-weinberg": {
      "name": "Stephen Weinberg"
    },
    "sukhman-virk": {
      "name": "Sukhman Virk"
    },
    "sumaira-nazir": {
      "name": "Sumaira Nazir"
    },
    "sumer": {
      "name": "Sümer Cip"
    },
    "syed-raza": {
      "name": "Syed Raza"
    },
    "tamara-bacchia": {
      "name": "Tamara Bacchia"
    },
    "tara-arnold": {
      "name": "Tara Arnold"
    },
    "theosakamg": {
      "name": "Mickael Gaillard",
      "github": "theosakamg"
    },
    "thomasdiluccio": {
      "name": "Thomas di Luccio"
    },
    "tim-anderson": {
      "name": "Tim Anderson"
    },
    "tom-helmer-hansen": {
      "name": "Tom Helmer Hansen"
    },
    "tylermills": {
      "name": "Tyler Mills"
    },
    "upsun": {
      "name": "Upsun"
    },
    "veronika-tolkachova": {
      "name": "Veronika Tolkachova",
      "linkedin": "https://www.linkedin.com/in/veronika-tolkachova-169167a2"
    },
    "vince-parker": {
      "name": "Vince Parker"
    },
    "vinnie-russo": {
      "name": "Vincenzo Russo"
    },
    "vrobert78": {
      "name": "Vincent Robert",
      "github": "vrobert78",
      "linkedin": "https://www.linkedin.com/in/vincent-robert-498a883"
    },
    "yuriy-babenko": {
      "name": "Yuriy Babenko"
    },
    "yuriy-gerasimov": {
      "name": "Yuriy Gerasimov"
    }
  };
  return <div className="post-meta">
      {(authors.length > 0 || formattedDate) && <div className="post-meta-info">
          {authors.length > 0 && <div className="post-meta-authors">
              {authors.map(slug => {
    const {name, url, avatarUrl} = resolveAuthor(slug);
    const inner = <>
                    {avatarUrl && <img src={avatarUrl} alt={name} className="post-meta-avatar" />}
                    <span className="post-meta-author-name">{name}</span>
                  </>;
    return url ? <a key={slug} href={url} target="_blank" rel="noopener noreferrer" className="post-meta-author">
                    {inner}
                  </a> : <span key={slug} className="post-meta-author">{inner}</span>;
  })}
            </div>}
          {authors.length > 0 && formattedDate && <span className="post-meta-separator" aria-hidden="true">·</span>}
          {formattedDate && <span className="post-meta-date">{formattedDate}</span>}
        </div>}
      {image && <img src={image} alt="" className="post-meta-image" aria-hidden="true" />}
    </div>;
};

<PostMeta data={{ author: ["flovntp"], date: "2024-10-07T08:30:05-04:00", image: "/images/posts/how-tos/blackfire-google-alert-markers/google-alert-marker.webp" }} />

Observing your application is essential to be able to find what needs (and what does not need) to be optimized.
Understand your traffic spike is key but when an external source lead to those traffic spikes, that's not any easy path to find out the exact reason.

As an example, your CEO talks about your brand on a TV show, this automatically leads to a raise of your traffic few seconds later and by ricochet, potential performance issues: that's what I call in this article the `TV show effect`.
Other names can be found for these external events leading to traffic spikes: [Slashdot effects](https://en.wikipedia.org/wiki/Slashdot_effect) or [Hug of death](https://nordvpn.com/fr/cybersecurity/glossary/hug-of-death/).
Of course, most of the requests on your website would be absorbed by your reverse proxy in front (Varnish, Faslty,...), but some of those requests won't and would lead to an increase of the resource consumption.

[Blackfire](https://blackfire.io) is a great Application Performance Monitoring (APM) tool to observe and gather data about consumed server resources like memory, CPU time, and I/O operations.
On the Monitoring section of your Blackfire dashboard, the [timeline](https://docs.blackfire.io/profiling-cookbooks/understanding-timelines) helps you to analyse the behavior of your application, by giving a lot of metrics in a specific timeframe of your traffic.
[Blackfire](https://blackfire.io) also provides a great (and not so well known) feature to spot markers on this timeline.
These markers are a way to add more context for your observers to better understand what's going on (when and why?).

We saw in a previous tutorial [How to automatically spot markers on your Blackfire timeline for any occuring Upsun infrastructure processes](https://dev.to/upsun/how-to-get-notifications-when-an-upsun-infrastructure-event-occurs-2257).
In this current tutorial, I will showcase how you can import any new [Google alerts](https://www.google.fr/alerts) that pops up and spot markers on your Blackfire timeline.
As a summary for this tutorial, we will import, via a cron, any new RSS Feed entries in a [Symfony](https://www.symfony.com) database (SQLite), and spot corresponding markers on your Blackfire timeline.

<Note>
  🚨 **Please note:**
  I choose to use [Google alerts](https://www.google.fr/alerts) (RSS Feed) as it's an easy way to get notification when a new article/page is added in their index
  but that's just an example.
  Everything related to Google alerts in this article can easily be adapted to another RSS Feed structure.
  You would just need to adapt the PHP code to import RSS entries (function `createFeed` from class `src/Services/BlackfireGoogleAlert.php`) to your new RSS Feed structure.
</Note>

## Google alerts and Blackfire markers

<Note>
  **Assumptions:**

  * You already have a [Google Account](https://www.google.com)
  * You already have a [GitHub account](https://github.com/)
  * You already have an Upsun account. If you don’t, [please register for a trial account](https://auth.upsun.com/register). You can sign up with an email address or an existing GitHub, Bitbucket, or Google account. If you choose one of these accounts, you can set a password for your Upsun account later.
  * You have the [Upsun CLI](https://docs.upsun.com/administration/cli.html) installed locally.
  * (optional) Your local environment is compatible with [Symfony requirements](https://symfony.com/doc/current/setup.html)
</Note>

### Create a new Google alert RSS Feed

The first step is to create a new RSS Feed alert.

To do so, connect to your Google account and go on [Google alerts](https://www.google.fr/alerts) page and follow this [official tutorial](https://support.google.com/websearch/answer/4815696?hl=en).
Please fill it with the following options:

<img src="https://mintcdn.com/upsun-c9761871/tziXiwEbbKjwbX3l/images/posts/how-tos/blackfire-google-alert-markers/google-alert-form.webp?fit=max&auto=format&n=tziXiwEbbKjwbX3l&q=85&s=97df5d51a4f25828f0adb57dac941ab4" alt="Google alert form with RSS Feed options" width="1332" height="918" data-path="images/posts/how-tos/blackfire-google-alert-markers/google-alert-form.webp" />

### Create a fork of the `blackfire-google-alert-example` repository

To ease your work, I already developed a small Symfony application that contains the minimum codebase for importing all feeds from a Google Alert RSS Feed in your database and spotting corresponding markers on your Blackfire timeline, using a [cron](https://docs.upsun.com/create-apps/app-reference/single-runtime-image.html#crons).

You can start by [creating a fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) of this [`blackfire-google-alert-example` GitHub repository](https://github.com/upsun/blackfire-google-alert-example) in your own GitHub organization for later usage.

<Note>
  🚨 **Please note**: for this tutorial, we use a [Symfony](https://www.symfony.com) application but feel free to reuse the business logic and come up with a solution using any other framework you like.
</Note>

The source code in this GitHub repository is already Upsun-ready.
After creating a fork, you can clone it locally and [create an empty Upsun project](https://docs.upsun.com/get-started/here/create-project.html).
Use the 3 following command lines from the root of your source code, and follow prompts:

```bash {filename="Terminal"} theme={null}
git clone git@github.com:<OWNER/REPOSITORY>
cd <REPOSITORY>
upsun project:create
```

Final output should be:

```bash {filename="Terminal"} theme={null}
  ...
  The Upsun Bot is activating your project

      ▄     ▄  
      ▄█▄▄▄█▄  
    ▄██▄███▄██▄
    █ █▀▀▀▀▀█ █
       ▀▀ ▀▀   

  The project is now ready!
  <PROJECT_ID>
```

* `<OWNER/REPOSITORY>` is your forked `<REPOSITORY>` in GitHub.
* `<PROJECT_ID>`: keep this project ID in mind for later use

At this step, your Upsun project is not deployed yet.

We need first to pass to the application your previously created RSS Feed uri via an environment variable.

### Define your RSS Feed uri in an environment variable

Before deploying, please note that this `blackfire-google-alerts` app need you to define an environment variable for your RSS Feed uri (`<GOOGLE_RSS_ALERT_URI>`).

For local usage of this application, you can define this `GOOGLE_RSS_ALERT ` environment variable in a `.env` file:

```shell {filename=".env"} theme={null}
GOOGLE_RSS_ALERT=<GOOGLE_RSS_ALERT_URI>
```

As Upsun projects does not import `.env` file (local Symfony server), there are 3 ways to define environment variables for your Upsun environment/project.
<br />You can either:

* use an [`.environment` file](https://docs.upsun.com/development/variables/set-variables.html#testing-environment-scripts)
  ```shell {filename=".environment"} theme={null}
  GOOGLE_RSS_ALERT=<GOOGLE_RSS_ALERT_URI>
  ```
  Then you need to add this file to your Git history
  ```
  git add .environment && git commit -m "adding GOOGLE_RSS_ALERT envVar"
  ```

* use the [Upsun CLI](https://docs.upsun.com/administration/cli.html).

```bash {filename="Terminal"} theme={null}
upsun variable:create --level project --name GOOGLE_RSS_ALERT --prefix env --value <GOOGLE_RSS_ALERT_URI>
```

* define it from the [Upsun Console](https://console.upsun.com): to do so, please follow steps described in the [corresponding documentation page](https://docs.upsun.com/development/variables/set-variables.html#create-project-variables) (`In the Console` tab)

### Add a source integration to your fork

Then, you need to [add a source integration](https://docs.upsun.com/integrations/source/github.html) to sync your Upsun project with your new forked GitHub repository.

```bash {filename="Terminal"} theme={null}
upsun integration:add --type github --repository <OWNER/REPOSITORY> --token <GITHUB_ACCESS_TOKEN> [--project <PROJECT_ID>] 
```

* `<PROJECT_ID>` is the ID of your new `blackfire-google-alert` project ID (from `upsun project:create` command).
* `<GITHUB_ACCESS_TOKEN>` is your [generated GitHub API Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens).

<Note>
  For more information about source integration, please refer to the [enable source integration documentation page](https://docs.upsun.com/integrations/source/github.html#2-enable-the-integration).
</Note>

### Blackfire Timeline testing

From the [Upsun Console](https://console.upsun.com) of your project, wait for the first deploy to be completed (\~3 minutes) and then click on `Launch Blackfire` button on the left menu.

<img src="https://mintcdn.com/upsun-c9761871/tziXiwEbbKjwbX3l/images/posts/how-tos/blackfire-google-alert-markers/google-alert-button.webp?fit=max&auto=format&n=tziXiwEbbKjwbX3l&q=85&s=026cf329ffdb3285fc1d479c60c71938" alt="" width="682" height="304" data-path="images/posts/how-tos/blackfire-google-alert-markers/google-alert-button.webp" />

<Note>
  🚨 **Please note**: If you would like to automatically open your current project Console dashboard from your terminal, you can use this command from the root of your source code:

  ```bash {filename="Terminal"} theme={null}
  upsun web
  ```
</Note>

You then land on the Blackfire dashboard (Health report).
CLick on the `Monitoring` menu item on the left.
Markers should appear on the timeline, such as:

<img src="https://mintcdn.com/upsun-c9761871/tziXiwEbbKjwbX3l/images/posts/how-tos/blackfire-google-alert-markers/google-alert-timeline.webp?fit=max&auto=format&n=tziXiwEbbKjwbX3l&q=85&s=b8176743cdcac62e9ee254a0b12625cd" alt="" width="1902" height="788" data-path="images/posts/how-tos/blackfire-google-alert-markers/google-alert-timeline.webp" />

<Note>
  🚨 **Please note**: The `blackfire-google-alerts` app imports RSS Feed entries via a cron that runs every 5 minutes, so depending on the timing, you probably need to wait a bit for the first import to take place.
  <br />If you want to manually trigger the first import, you can also execute this command:

  ```bash {filename="Terminal"} theme={null}
  upsun ssh 'php bin/console blackfire:import-google-alerts'
  ```
</Note>

## Conclusion

Adding more context to your Observability timeline is crucial if you are running applications with a lot of traffic and if you want to be able to detect what i called `TV show effects`.

In this guide, we took a look at how to automatically add context to your Blackfire timeline using an external source (here, Google Alert RSS Feed).
Some next steps could include importing any other external source of information that could explain traffic spikes on your application,
or give a way for your marketing team to manually spot incoming events on your Blackfire timeline?

* In 2 weeks, your CEO is willing to be at a TV show, talking about your brand?
* A new 1 month nurturing campaign is planned in 3 weeks (start|end markers?)?

Possibilities are infinite, and all of these features to add context on your Blackfire timeline would help a lot your observers/developers to analyse your traffic and understand why and when a performance bottleneck is happening.

I keep you posted, I will soon write another episode to go further on contextualisation of your observability strategy.

Remain up-to-date on the latest from us over on our social media and community channels:
[DEV.to](https://dev.to/upsun), [Reddit](https://www.reddit.com/r/upsun/),
and the [Upsun Community Forum](https://community.upsun.com/).
