.net Core 2.2 API logging for front-end in Serilog

The setup is a Front-end and a Back-end. Logging for Back-end in Serilog is quite straight forward. Things got a bit hairy when it was required to supply an API for logging Front-end errors and distinguish between the two in the Log table. In this post will show how this was achieved.

The project is same like the one in this post, however this is simpler so creating a repository for this is not required.

Create a LogController, this will be used to log from the front-end. Setup the action how you wish to send your errors and level of detail.

        // POST: api/v1/Log
        [HttpPost]
        public void Post([FromBody] string value)
        {
            Log.Information(value);
        }

Now to distinguish between back-end generated logs, an Enricher is used.

    public class LocationEnricher : ILogEventEnricher
    {
        public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
        {
            try
            {
                if(logEvent.Properties["RequestPath"].ToString().Contains(@"/api/v1/Log/"))
                    logEvent.AddOrUpdateProperty(propertyFactory.CreateProperty("Location", "FrontEnd"));
                else
                    logEvent.AddOrUpdateProperty(propertyFactory.CreateProperty("Location", "BackEnd"));
            }
            catch
            {
                // ignored
            }
        }
    }

What this does is check that this is coming from /api/v1/Log path and if so sets “Location” property to Frontend, else Backend.

 
              .UseSerilog((context, config) =>
              {
                  config.ReadFrom.Configuration(context.Configuration);
                  config.Enrich.WithLocation();
              })

Add the column to the appconfig.json

  "columnOptionsSection": {
            "customColumns": [
              {
                "ColumnName": "Location",
                "DataType": "nvarchar",
                "DataLength": 20,
                "AllowNull": true
              }
     ]
  }

This is pretty much it, easy eh?

One might get more sophistication in the logging from Front-end and distinguish the type of logic.

        // POST: api/v1/Log
        // Do some magic here ;)
        [HttpPost]
        public void Post([FromBody] string value)
        {
            Log.Information(value);
            Log.Debug(value);
            Log.Error(value);
            Log.Warning(value);
            Log.Fatal(value);
        }

But that is outside of the scope of this post.

Angular 2+ Modals

Although this article makes some valid points in building modals it doesn’t provide a working example.

So here is the working example on Stackblitz of what is explained. some things were renamed to make better sense.

I do not agree with everything that was done. Accessing the DOM directly in DomService is not the ideal and can come up with consequences.

The correct way to access elements is by using a template variable as below

 <input type="text" #testElem>

Then get the reference of the element

 @ViewChild('testElem') el:ElementRef;

Then access your native element as below

 this.el.nativeElement.style.background = "red";

More about it here

There are more elaborate and costomizable ways to create modals. This was just an exercise in rolling your own solution.

Angular 7 Progress Indicator Component

I came across the following progress indicator for jquery and wanted to try to recreate it as an angular component.

<ol class="ProgressBar">
    <li #lis class="ProgressBar-step" *ngFor="let step of steps; let i = index">
      <svg class="ProgressBar-icon"><use attr.href="{{svgUrl}}"/></svg>

      <span class="ProgressBar-stepLabel">{{step}}</span>
    </li>
</ol>

The only modifications done to the HTML were adding a template variable #lis and iterating on the steps to be displayed.

xlink:href has been deprecated so it was replaced with attr.href and the svg was saved in a file and the path to the file is passed to the component. Styling is pretty much identical.

import { Component, OnInit } from '@angular/core';
import { ElementRef, QueryList, ViewChildren, Renderer2} from '@angular/core';
import { Input } from '@angular/core';

@Component({
  selector: 'app-progress-indicator',
  templateUrl: './progress-indicator.component.html',
  styleUrls: ['./progress-indicator.component.scss'],
})
export class ProgressIndicatorComponent {

  @Input() public steps: string[];
  @Input() public svgUrl:string;

  @ViewChildren('lis') lis: QueryList<ElementRef>;

  private elems: ElementRef<any>[];

  constructor(private renderer: Renderer2) {
  }

  advance() {
    this.elems = this.lis.toArray();
    var count = this.lis.filter(x => x.nativeElement.classList.contains('is-current')).length;
    if (count > 0) {
      let index = 0;
      for (let i = 0; i < this.elems.length; i++) {
        if (this.elems[i].nativeElement.classList.contains('is-current')) {
          index = i;
          break;
        }
      }
      if (index < this.elems.length) {
        if(index <  this.elems.length-1){ // if last one done remove current
             this.renderer.removeClass(this.elems[index].nativeElement, "is-current");
        }
        this.renderer.addClass(this.elems[index].nativeElement, "is-complete");
      } 
      if (index +1 < this.elems.length) {
        this.renderer.addClass(this.elems[++index].nativeElement, "is-current");
      }
    } else {
      this.renderer.addClass(this.lis.first.nativeElement, "is-current");
    }
  }

  previous(){
    this.elems = this.lis.toArray();
    var count = this.lis.filter(x => x.nativeElement.classList.contains('is-current')).length;
    if (count > 0) {
      let index = 0;
      for (let i = 0; i < this.elems.length; i++) {
        if (this.elems[i].nativeElement.classList.contains('is-current')) {
          index = i;
          break;
        }
      }
      if (index >= 0) {
         this.renderer.removeClass(this.elems[index].nativeElement, "is-current");
         this.renderer.removeClass(this.elems[index].nativeElement, "is-complete");
      }
      if(index > 0){
         this.renderer.addClass(this.elems[--index].nativeElement, "is-current");
        this.renderer.removeClass(this.elems[index].nativeElement, "is-complete")
      }
    }
  }
}

The component uses @ViewChildren to access the li tags and Renderer2 to do safe modifications to the classes of each element.

 <app-progress-indicator 
     [steps]='steps'  
     [svgUrl]='"./assets/svg/checkmark-bold.svg#checkmark-bold"'>
</app-progress-indicator>

Usage is as above steps should be an array of strings.

  public steps = ["Test","Review","Result","Mistakes"]; 

Note that QueryList is not accessible by index nor one could iterate on it for ES5 so toarray() was used to get things done. ES6 makes it possible to iterate directly.

JavaScript Copy Text in ClipBoard

The Following lets a button click cause text data to be pushed inside the clipboard. This is because direct access to clipboard from JavaScript is not possible for security reasons.

<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML>
   <HEAD>
      <TITLE>
         Copy Command
      </TITLE>
   </HEAD>
   <BODY>
      <script>
         //<![CDATA[
         function copy(){
         	 
         var textarea = document.createElement("textarea");

         var str = 
             ['One,One',
              'Two,Tow',
              'Three,Three',
              'Four,Four',
              'Five,Five',
             ].join('\n');
         
         
         textarea.value=str;
         textarea.setAttribute('id',"t1");
         document.body.appendChild(input);
         var input = document.getElementById("t1");
         input.focus();
         input.select();
         document.execCommand('copy');
         document.body.removeChild(input);
         
         }
         //]]>
      </script> 
      <button onclick="copy();" id="copy">copy</button>
   </BODY>
</HTML>

 

Tested on IE, FF and Chrome.

Using GraphAPI From WebAPI

I will Polish this post later, meanwhile:

Go In Azure Register an APP

  • Get Client ID:
  • Generate Key
  • Permissions to other application: Read Directory Data ( Very important else nothing will work)

Packages Required:

https://www.nuget.org/packages/Microsoft.Azure.ActiveDirectory.GraphClient/2.0.5

https://www.nuget.org/packages/Microsoft.IdentityModel.Clients.ActiveDirectory/2.14.201151115

 

Web.config

<appSettings><add key=”ida:ClientID” value=”a69ce710-2edc-4cf2-bf40-ded19bb28e08″ />
<add key=”ida:AppKey” value=”KEY GENERATED FROM AZURE” />
<add key=”ida:GraphUrl” value=”https://graph.windows.net” />
<add key=”ida:authString” value=”https://login.windows.net/stephangaleagmail.onmicrosoft.com” />
<add key=”ida:graphURL” value=”https://graph.windows.net/stephangaleagmail.onmicrosoft.com” /></appSettings>

 

Getting an instance of ADClient

private static async Task<string> GetAppTokenAsync()
{
AuthenticationContext authenticationContext = new AuthenticationContext(ConfigurationManager.AppSettings[“ida:authString”], false);
ClientCredential clientCred = new ClientCredential(ConfigurationManager.AppSettings[“ida:ClientId”], ConfigurationManager.AppSettings[“ida:AppKey”]);
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenAsync(ConfigurationManager.AppSettings[“ida:GraphUrl”], clientCred);
return authenticationResult.AccessToken;
}

Uri serviceRoot = new Uri(ConfigurationManager.AppSettings[“ida:graphURL”]);
ActiveDirectoryClient adClient = new ActiveDirectoryClient(
serviceRoot,
async () => await GetAppTokenAsync());

 

Looking up a user
var upn = “test@stephangaleagmail.onmicrosoft.com”;
var userLookup = adClient.Users.Where(
user => user.UserPrincipalName.Equals(
upn, StringComparison.CurrentCultureIgnoreCase)).ExecuteSingleAsync();

User user1= (User)await userLookup;

 

Gulp + Livereload Occupied port Issue

Sometimes when using Gulp with Livereload when one wishes to stop it and restarting it Ctrl+c  and gulp still holds the port occupied resulting in the following error.

… Uhoh. Got error listen EADDRINUSE :::35729 …
Error: listen EADDRINUSE :::35729
at Object.exports._errnoException (util.js:856:11)
at exports._exceptionWithHostPort (util.js:879:20)
at Server._listen2 (net.js:1234:14)
at listen (net.js:1270:10)
at Server.listen (net.js:1366:5)
at Server.listen (/var/www/angle.local/master/node_modules/tiny-lr/lib/serve                   r.js:162:15)
at Function.exports.listen (/var/www/angle.local/master/node_modules/gulp-li                   vereload/gulp-livereload.js:68:12)
at Gulp.<anonymous> (/var/www/angle.local/master/gulpfile.js:282:16)
at module.exports (/var/www/angle.local/master/node_modules/orchestrator/lib                   /runTask.js:34:7)
at Gulp.Orchestrator._runTask (/var/www/angle.local/master/node_modules/orch                   estrator/index.js:273:3)

You already have a server listening on 35729
You should stop it and try again.

 

A quick solution is to kill the process by port with the following command

kill -9 $(lsof -t -i :35729)

 

 

 

Installing Webmin on CentOS 7 and configure firewall

Some days ago I described the steps involved in configuring a CentOS WebServer VM with virtual hosts here manually. In this post I will describe the steps involved to install Webmin .

Creating a repository file for Webmin

cd /etc/yum.repos.d/

sudo vi webmin.repo
[Webmin]
name=Webmin Distribution Neutral
#baseurl=http://download.webmin.com/download/yum
mirrorlist=http://download.webmin.com/download/yum/mirrorlist
enabled=1
Esc
:wq

Install Webmin GPG key

rpm --import http://www.webmin.com/jcameron-key.asc

Update Repositories

yum check-update

Update Repos
Update Repos

Install Webmin

yum -y install webmin

Add to startup

chkconfig webmin on

Start the service

service webmin start

Enable webmin port in firewall

firewall-cmd --permanent --add-port=10000/tcp
firewall-cmd --reload

 

Open in browser

https://192.168.101.39:10000/ where 192.169.101.39 is your VM IP (ip addr)

 

Trust and Add
Trust and Add

Add exception
Add exception

Webmin Login
Webmin Login

 

Use same credentials used to admin the VM

Webmin Dashboard
Webmin Dashboard