AppBeat Blog

Perfect Monitoring for Your Cloud

How to get notification before SSL certificate expires?

Login to your AppBeat account (or create new one if you don't have it yet).

Click "Add new periodic monitor" and select "SSL Certificate" as shown on screenshot below:

Click "Next" button and enter your URL:

Click "Next" and close wizard with "Finish".

Your certificate will now be periodically monitored and when it is about to expire (30 days or less - you can change this in check settings), you will automatically receive notification (we support email, SMS, Slack, web hooks, ...).

Simple as that!

Want less monitoring alarms? Introducing quiet hours!

In newest AppBeat version 1.7.0 you can now define quiet hours for your users, services and/or third-party integrations (I will refer to these as "resources").

Quiet hours are recurring time periods during which alerts (outgoing notifications) are muted for selected resource(s). Quiet hours does not affect monitoring, just notifications. This means that you will always be able to check log entries, even during effective quiet hours.

You can access this new feature from "Alerting" module and by clicking "Quiet hours" tab as shown below:

When you click "Add quiet hours for" button for specific resource, you will be presented with dialog similar to this:

You can add multiple quiet hours for same resource, AppBeat engine will always aggregate those entries while determining if quiet hour is effective or not.

Here are some use case examples:

  • you have service which is not operational 24/7 - now you can simply set quiet hours when service is not active so you don't receive any alerts
  • you have recurring (nightly) updates and you want to mute alerts during this period
  • you don't want to receive alerts for less important service(s) during weekend
  • your team member goes on vacation or sick leave and you don't want to bother him/her with outage alerts
  • some of your team members receive alerts only during working hours (and you have dedicated team for 24/7 operations)
  • etc.

Happy monitoring!

Receive monitoring alerts to your Workplace by Facebook

Today we published new application version with native support for Facebook Workplace.

How to enable it? Please login to your Facebook Workplace account with administrative rights.

Click on Dashboard / Integrations and click "Create App" button. Enter "App Name" (it can be AppBeat or something similar) and set minimum App Permissions to: "Read content" (needed to read group id) and "Post to groups" (needed to post alert).

Now go to AppBeat and click "Alerting" from left menu. Select "Third-Party service integration" tab, click "Add new integration" button and select "Workplace by Facebook":

Please enter following fields:

  • arbitrary integration name (seen only in AppBeat)
  • access token (you should get this when you create Workplace App as described above)
  • community id (available from Facebook Workplace Dashboard / Integrations)
  • group (group name which is available in Facebook Workplace; you can also leave default group named "General")

We suggest that you click "Test" button and check for any errors. If everything goes OK, you should see new test post in your Workplace group.

Finally, don't forget to associate your new Facebook Workplace integration with your AppBeat service. You can do this by clicking "Services & Checks" from left menu, double-click on service that you want to link, select "Notification types" tab, enable (check) your new integration and click "Save" button.

Optionally you can repeat this for multiple services.

Hopefully you will find these instructions useful. And as always - happy monitoring!

How to create status page for your app or website using your domain

AppBeat website monitoring allowed you to easily create public status page for a long time. This page is hosted on our domain by using URL in following format: https://appbeat.io/status/YOUR_STATUS_PAGE_ID

However some users wanted more:

  • hosting public status page on their own domain
  • use their own CSS styling

We are happy to announce that newest AppBeat allows you all that!

Method 1: creating public status page on your domain by using simple static HTML template and JavaScript

If you navigate to "AppBeat / Public status / Dashboard" you will be able to enable your status page from there and publish on your web server static HTML file, similar to this:

<html>
   <head>
      <title>Your title</title>
      <!--Please copy css style to your web server because we could rename it in future-->
      <link rel="stylesheet" type="text/css" href="https://appbeat.io/assets/css/status-page.css" />
   </head>
   <body>
      <!--AppBeat status page is rendered into element with id appbeat-status-container-->
      <div id="appbeat-status-container" class="status-layout"></div>

      <!--Load AppBeat status page script for your page and start rendering it-->
      <script src="https://web1.appbeat.io/embed-status/YOUR_STATUS_PAGE_ID"></script>
      <!--Tip: your script is also duplicated on https://web2.appbeat.io which is hosted in different region-->
   </body>
</html>

Every time your visitor navigates to this page it will show him/her latest status of your page.

Method 2: creating public status page on your domain by getting embeddable HTML fragment on your server side

If you don't want to use JavaScript based solution for your status page, you can call following endpoints on your web server to get HTML fragment which can be embedded in your HTTP response:


Both methods can be styled with your own CSS or you can use our default style located at: https://appbeat.io/assets/css/status-page.css

Good thing about this solution is also, that you don't have to change DNS settings to get it to work. Simply publish static HTML file on your web server or embed our HTML fragment if you use dynamic pages. And that is it!

If you would like to try this feature, you can register for new free account and please contact us so we can enable your 14-day risk free trial.

Please note, that for limited time we offer you 60% discount on Starter plan.

How to build .NET Microservices

We would like to share with you very interesting NDC Conference session from Sydney which describes pros and cons of a microservices solution and how you can build one using .NET and a number of open source tools like EventStore, RabbitMq and Redis.

Presentation was done by Richard Banks and you can watch it on YouTube:

C# method for very fast and efficient traceroute (network diagnostic tool)

We like C#, .NET and .NET Core!

Last time we played a little bit with traceroute utility and thought, could we write something fast, efficient, and since we love Asynchronous Programming with async and await (C#), something that would include this pattern as well?

Here is what we came up with :)

using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;

namespace AppBeat.Core.Utility
{
    public class TraceRoute
    {
        private const string DATA = "AppBeat Monitoring - https://appbeat.io - AppBeat Monitoring"; //60 Bytes
        private static readonly byte[] _buffer = Encoding.ASCII.GetBytes(DATA);
        private const int MAX_HOPS = 15;
        private const string STR_REQUEST_TIMEOUT = "Request timed out.";
        private const string STR_REQUEST_TIME_NA = "*";
        private const int REQUEST_TIMEOUT = 4000;

        /// <summary>
        /// Runs traceroute and writes result to console.
        /// </summary>
        public static async Task TryTraceRouteAsync(string hostNameOrAddress)
        {
            EnsureCommonArguments(hostNameOrAddress);
            Contract.EndContractBlock();

            using (var console = Console.OpenStandardOutput())
            using (var sw = new StreamWriter(console))
            {
                await TryTraceRouteAsync(hostNameOrAddress, sw);
            }
        }

        /// <summary>
        /// Runs traceroute and writes result to provided stream.
        /// </summary>
        public static async Task TryTraceRouteAsync(string hostNameOrAddress, StreamWriter outputStreamWriter)
        {
            EnsureCommonArguments(hostNameOrAddress);
            if (outputStreamWriter == null)
            {
                throw new ArgumentNullException(nameof(outputStreamWriter));
            }
            Contract.EndContractBlock();

            await outputStreamWriter.WriteLineAsync($"traceroute to {hostNameOrAddress}, {MAX_HOPS} hops max, {_buffer.Length} byte packets");

            //dispatch parallel tasks for each hop
            var arrTraceRouteTasks = new Task<TraceRouteResult>[MAX_HOPS];
            for (int zeroBasedHop = 0; zeroBasedHop < MAX_HOPS; zeroBasedHop++)
            {
                arrTraceRouteTasks[zeroBasedHop] = TryTraceRouteInternalAsync(hostNameOrAddress, zeroBasedHop);
            }

            //and wait for them to finish
            await Task.WhenAll(arrTraceRouteTasks);

            //now just collect all results and write them to output stream
            for (int hop = 0; hop < MAX_HOPS; hop++)
            {
                var traceTask = arrTraceRouteTasks[hop];
                if (traceTask.Status == TaskStatus.RanToCompletion)
                {
                    var res = traceTask.Result;
                    await outputStreamWriter.WriteLineAsync(res.Message);

                    if (res.IsComplete)
                    {
                        //trace complete
                        break;
                    }
                }
                else
                {
                    await outputStreamWriter.WriteLineAsync($"Could not get result for hop #{hop + 1}");
                }
            }
        }

        private static void EnsureCommonArguments(string hostNameOrAddress)
        {
            if (hostNameOrAddress == null)
            {
                throw new ArgumentNullException(nameof(hostNameOrAddress));
            }

            if (string.IsNullOrWhiteSpace(hostNameOrAddress))
            {
                throw new ArgumentException("Hostname or address is required", nameof(hostNameOrAddress));
            }
        }

        public class TraceRouteResult
        {
            public TraceRouteResult(string message, bool isComplete)
            {
                Message = message;
                IsComplete = isComplete;
            }

            public string Message
            {
                get; private set;
            }

            public bool IsComplete
            {
                get;private set;
            }
        }

        public static async Task<TraceRouteResult> TryTraceRouteInternalAsync(string hostNameOrAddress, int zeroBasedHop)
        {
            using (Ping pingSender = new Ping())
            {
                var hop = zeroBasedHop + 1;

                PingOptions pingOptions = new PingOptions();
                Stopwatch stopWatch = new Stopwatch();
                pingOptions.DontFragment = true;
                pingOptions.Ttl = hop;

                stopWatch.Start();

                PingReply pingReply = await pingSender.SendPingAsync(
                    hostNameOrAddress,
                    REQUEST_TIMEOUT,
                    _buffer,
                    pingOptions
                );

                stopWatch.Stop();

                var elapsedMilliseconds = stopWatch.ElapsedMilliseconds;

                string pingReplyAddress;
                string strElapsedMilliseconds;

                if (pingReply.Status == IPStatus.TimedOut)
                {
                    pingReplyAddress = STR_REQUEST_TIMEOUT;
                    strElapsedMilliseconds = STR_REQUEST_TIME_NA;
                }
                else
                {
                    pingReplyAddress = pingReply.Address.ToString();
                    strElapsedMilliseconds = $"{elapsedMilliseconds.ToString(System.Globalization.CultureInfo.InvariantCulture)} ms";
                }

                var traceResults = new StringBuilder();
                traceResults.Append(hop.ToString(System.Globalization.CultureInfo.InvariantCulture).PadRight(4, ' '));
                traceResults.Append(strElapsedMilliseconds.PadRight(10, ' '));
                traceResults.Append(pingReplyAddress);

                return new TraceRouteResult(traceResults.ToString(), pingReply.Status == IPStatus.Success);
            }
        }
    }
}

Method above basically dispatches 15 parallel (asynchronous) tasks and waits for their result. For this reason it is really fast compared to standard implementation, which would do only one step at a time, and wait for each step (15 times in a row).

Hopefully you will find this useful :)

Better website monitoring experience for your team

We deployed our upgraded rule engine. You can now have more descriptive notifications for Warning and Error rules, by combining them together with custom note:

[trigger_rule: my notification message: why so slow???]
%RESPONSE_TIME% > 10000

[trigger_rule: server X needs restart???]
%STATUS% = 503 AND MATCH(%RESPONSE%, "YOUR_REGULAR_EXPRESSION")

If conditions for trigger are met, you will automatically receive notification with your note. This may be useful in critical situtations for your team, so they can respond more quickly!

Happy monitoring!

Coming soon: multi expression rules for better monitoring experience

Very soon we will add multi expression support for Warning and Error notification rules. Syntax will be as following:

[trigger_rule: YOUR_RULE_NAME_1]
EXPRESSION(S)_1

[trigger_rule: YOUR_RULE_NAME_2]
EXPRESSION(S)_2

[trigger_rule: YOUR_RULE_NAME_N]
EXPRESSION(S)_N

On user interface this will look something like this:

This example rule then triggers "Warning" notification which is sent to you and it is also visible in monitor log:

 

How to periodically run SSH commands and trigger AppBeat alert

In AppBeat 1.4.5 we introduced support for running SSH commands where you can trigger monitoring alert by checking result of:

  • command exit status code (for example you can simply trigger error if exit status code is not 0)
  • checking specific text in standard output (stdout) by running regex expression
  • checking specific text in standard error (stderr) by running regex expression

You can add new SSH monitor by clicking "Add new periodic monitor", enter monitor name and from "Agent type" select "Run Secure Shell (SSH) command":

On next step enter SSH hostname or server IP address and type command you would like to run (for example script name or cat /proc/uptime, ...) and specify server authentication. You can use standard username/password authentication or key based authentication (with private key in OpenSSH format).

After you finished "New periodic check" wizard we will immediately start monitoring your SSH server by running your command and checking for expected result.

Happy monitoring!

p.s. if you don't have AppBeat account yet, you can create one for free!