Solution architecture

1. Introduction

1.1 Mission statement

logger is a cloud-based logging application built to support development and monitoring factors.

Commercial alternatives are unnecessarily costly and complex, considering the lack of revenue from developing solely as a hobby.

At the same time, logger serves as a pilot for developing with the chosen technologies for factors.

1.2 Glossary

LogA list of entries that document events from the monitored application.
EntryConsist of a timestamp, a session id and a message.
TimestampShows when the event occurred.
SessionShows the session id, which helps isolate events from a particular user session if many sessions send events to the log.
MessageDescribes the occurred event.

1.3 References

loggerThe actual web application described in this document.logger
C#Object-oriented programming language.C# documentation
.NET CoreOpen-source platform for running apps on Windows, macOS and Linux..NET Core guide
RiderC# development tool from JetBrains.Rider
SmarterASP.NETASP.NET hosting on Windows 10 virtualized servers supporting the .NET Core runtime.SmarterASP.NET
factorsA monitored app logging to logger.factors
MaterialA user interface standard developed by Google.Material Design
DevExtremeA user interface component suite developed by DevExpress.DevExtreme
JavaScriptWeb page programming language supported by all widely used web browsers.W3Schools

1.4 Releases

VersionRelease dateDescription
1.0January 2020Initial release.

2. Solution overview

logger runs on a cloud-based virtualized Windows 10 server on top of IIS and the .NET Core runtime:

logger shows a simple list of log entries collected from another monitored app like factors.

3. Functionality

The solution supports viewing the log, pinging it to verify readiness and clearing it:

4. Information

The application basically holds a list of entries:

Each entry consists of a timestamp, a browser session id and a message.

5. Design constraints

5.1 Life span

The solution is needed as long as factors is in development and later in operation.

5.2 Policies

Minimize costsLong running hobby development doesn’t warrant expensive licenses or subscriptions.Since a platform for factors is already chosen, logger targets the same platform to avoid extra costs.

5.3 Standards

MaterialChoosing a widely used user interface standard is likely to make logger more approachable to new users.The user interface is built using the DevExtreme component suite.

5.4 Technologies

Web applicationWeb applications are accessible to everyone everywhere on all platforms.logger is built using a technology that makes it easy to develop and deploy web applications.
.NET CoreCross-platform that makes it easy to build web applications using C#.

Supported on many affordable hosting platforms.
logger is built using C#.
C#Modern feature-rich object-oriented programming language.logger is developed using a C# development tool.
RiderSubscription-based C# development tool with the same refactoring and coding aids as JetBrains ReSharper for VisualStudio.Imposes a monthly subscription cost, but is deemed worthwhile compared to the free VisualStudio, which requires a ReSharper subscription instead.
DevExtremeFeature-rich user interface component suite that supports Material.

Free for non-commercial use.
The free version can only supports JavaScript (not C# Razor pages).
JavaScriptRequired for using the non-commercial version of DevExtreme.logger won’t run in very old out-dated web browsers.
SmarterASP.NETAn affordable cloud-based platform offering virtualized Windows 10 servers that support .NET Core.Imposes a cost of an annual subscription.

6. Design principles

Support local testing om MacOS.logger is developed on a Mac and deployed on a Windows server.The data model is defined using an abstract class. One subclass that works on MacOS is instantiated when logger is run in development mode, and another subclass that only works on Windows is instantiated when logger is run in production mode.

7. Design qualities

7.1 Auditability

Since no sensitive data is created or modified in logger, accountability is not an issue.

7.2 Compliance

There is no applicable regulation to be compliant with.

7.3 Security

7.4 Integrity

Data integrity is maintained by strict concurrency management when adding new data.

7.5 Authentication

Not needed.

7.6 Authorization

Not needed.

7.7 Non-repudiation

Not necessary.

7.8 Usability

A simple user interface based on the widely acknowledged Material design ensured good usability.

The column headers support sorting to make it easier to read the log.

7.9 Concurrency

When running in development mode, only one instance of the application runs, and concurrency isn’t an issue.

When deployed in production, logger instantiates a shared memory buffer that is shared across all http servers instantiated by the ISS host.

To avoid multiple processes colliding when writing to the shared buffer, all updates first acquire a mutex before changing the buffer. This prevent concurrency problems that could endanger the data integrity.

7.10 Capacity

To avoid exhausting the server’s memory ressources, the buffer size is limited to 32768 bytes, which corresponds to 100-300 entries.

When the buffer fills up, oldest entries are discarded to make room for new entries.

7.11 Performance

Keeping log data in memory ensure highest possible performance when displaying log data and writing new entries.

The log view only refreshes data when the server-side buffer has been updated.

7.12 Observability

Not required – performance is easily observable when using the application, and since capacity is limited, performance doesn’t degrade as more entries are added.

7.13 Scalability

The buffer size is defined as a constant in the code. It can easily be increased, and a new version built and deployed in minutes.

7.14 Availability

The server shuts down logger after 20 minutes of inactivity, and any log entries in the buffer are discarded.

7.15 Testability

Since shared memory files aren’t supported by .NET Core on MacOS, the buffering logic is defined as an abstract class. When testing the application on MacOS, it instantiates a simple StringBuilder-based buffer that works well enough on MacOS.

The user interface also offers a ping command, which writes to the log in the same way as a monitored application would, demonstrating that it’s still capable of accepting and showing new entries.

7.16 Operability

The user interface offers a clear command that empties the log buffer.

7.17 Recoverability


8. High-level design

When the browser requests the home page from the web server, the web server (specifically IIS serving as proxy for the Kestrel server built into .NET Core on the Windows 10 server) starts a process that instantiates a PageModel to handle the incoming HTTP request.

The HttpServer startup code creates either a LocalData or GlobalData object depending on the environment. Both data classes are derived from the AbstractData class. The PageModel only knows the AbstractData class.

The PageModel parses the Index.cshtml Razor page into a pure HTML page, which is returned to the browser for rendering.

In the HTML page a JavaScript instantiates and initializes a dxDataGrid component, and binds it to the OnGetData() method on the PageModel. The dxDataGrid object calls the OnGetData() using AJAX GET to load the log data from the server.

The JavaScript code also set an interval timer that calls a refresh function every second while the web page is loaded in the browser.

The refresh function fetches the server’s data version. If the server’s version is different from the currently rendered local version, the dxDataGrid component is ordered to reload its data from the PageModel. Then, the local version is updated to avoid unnecessary new data loading until there is actually new data on the server.

When started by the IIS/Kestrel software on the web server, the Program class starts a process that instantiates the Index class and directs incoming HTTP requests to its methods.

The initialization code in the Startup class creates either a LocalData or GlobalData object, which is passed to the Index constructor.

The LocalData class uses a StringBuilder to hold the written log entries. The StringBuilder is local to the running process, so it can’t be shared by different processes serving different browser sessions. But it works fine for debugging the application during development.

The GlobalData uses a globally SharedMemoryFile to hold the written log entries. It’s accessible from every process, ensuring that all browser sessions view and write to the same central log.

SharedMemoryFile isn’t supported on Linux, which is the primary reason for using a the simple StringBuilder in the LocalData class.

9. Development

The application is developed on a MacBook running MacOS, which is basically a Linux adaption. The code is written in C#, HTML and JavaScript using Rider.

10. Testing

The application is designed run be fully functional when run on the developer machine. Rider is used to debug the running application during testing.

11. Deployment

The built code is deployed directly from within Rider to an FTP location, which is mapped on the cloud-based Windows 10 server to the application’s root folder.

The web application must be stopped before deployment to avoid problems when uploading new code files.

12. Operation

To check the application’s readiness, it’s easiest to just open it in any browser. Furthermore, it offers a ping button that writes a log entry, which verifies full operational capability.

The cloud-based Windows server is managed via the dashboard accessible on SmarterASP.NET’s website.

The IIS server and its application pool can be stopped and restarted to stop any running browser sessions when deploying a new version.