How to send HTTP request when "log.Fatal()" is executed? - http

In summary I want to send system information to my HTTP server when the "log.Fatal()" is called without any extra code for every log statement. Changing/overwriting the default behaviour of Info, Fatal etc. would be fantastic.
In Python, there is a way to add HTTP handlers to default logging library which in turn sends a POST HTTP request on log emit.

You can create a wrapper module for builtin log
yourproject/log/log.go
package log
import goLog "log"
func Fatal(v ...interface{}) {
goLog.Fatal(v...)
// send request ...
// reqQueue <- some args
}
replace log module with the wrapper in your project
// import "log"
import "yourproject/log"
func Foo() {
log.Fatal(err)
}

Try creating a type that wraps the standard Logger type, but with your desired enhancement. Then by creating an instance of it called "log" which wraps the default logger, you can continue to use logging in your code in the same way with minimal changes required (since it will have the same name as the log package, and retain *all of the methods).
package main
import _log "log"
type WrappedLogger struct {
// This field has no name, so we retain all the Logger methods
*_log.Logger
}
// here we override the behaviour of log.Fatal
func (l *WrappedLogger) Fatal(v ...interface{}) {
l.Println("doing the HTTP request")
/// do HTTP request
// now call the original Fatal method from the underlying logger
l.Logger.Fatal(v...)
}
// wrapping the default logger, but adding our new method.
var log = WrappedLogger{_log.Default()}
func main() {
// notice we can still use Println
log.Println("hello")
// but now Fatal does the special behaviour
log.Fatal("fatal log")
}
*The only gotcha here is that we've replaced the typical log package with a log instance. In many ways, it behaves the same, since most of the functions in the log package are set up as forwards to the default Logger instance for convenience.
However, this means that our new log won't have access to the "true" functions from the log package, such as log.New. For that, you will need to reference the alias to the original package.
// want to create a new logger?
_log.New(out, prefix, flag)

Related

How can I log every request/response body in Armeria HTTP client

I tried two approaches to log every HTTP body produced/received by my armeria client:
using out-of-box LoggingClient decorator
decorator(LoggingClient.newDecorator())
creating custom logging decorator
decorator { delegate, ctx, req ->
ctx.log().whenRequestComplete().thenAccept { log -> logger.trace(log.toStringRequestOnly()) }
ctx.log().whenComplete().thenAccept { log -> logger.trace(log.toStringResponseOnly()) }
delegate.execute(ctx, req)
}
But I see in logs only headers and other technical information. How can I log requestContent/responseContent?
It's said in armeria documentation that these fields are available only for Thrift clients:
the serialization-dependent content object of the request. ThriftCall for Thrift. null otherwise.
the serialization-dependent content object of the response. ThriftReply for Thrift. null otherwise.
It's weird to me.
I had to add com.linecorp.armeria.client.logging.ContentPreviewingClient decorator in addition to logging decorator:
decorator(ContentPreviewingClient.newDecorator(1000))

Execute generic Graph call in SPFx with PnP 3

So far I have been using the following to execute a request against the Graph API.
import { MSGraphClient } from '#microsoft/sp-http';
const graphClient: MSGraphClient = await context.msGraphClientFactory.getClient();
const uriGetAccessPackageAssignmentRequests = `/identityGovernance/entitlementManagement/accessPackageAssignments/filterByCurrentUser(on='target')?$select=id&$expand=accessPackageAssignmentResourceRoles&$filter=assignmentState ne 'Expired'`;
graphClient.api(uriGetAccessPackageAssignmentRequests).version('beta').get();
This has been working without any problems until I decided to change to #pnp/graph in the newest version (3.4.1).
I can use all the preset calls, but I cannot find a way to execute a generic graph call with a custom endpoint.
In all the tutorials they talk about
import { graph } from "#pnp/graph";
But if I do so, I get the following error -> Module '"#pnp/graph"' has no exported member 'graph'.
As I said, the described way in the documentation of using
import { graphfi, GraphFI, SPFx as graphSPFx } from '#pnp/graph'
export const getGraph = (context?: WebPartContext): GraphFI => {
if (_graph === null && context != null) {
//You must add the #pnp/logging package to include the PnPLogging behavior it is no longer a peer dependency
// The LogLevel set's at what level a message will be written to the console
_graph = graphfi().using(graphSPFx(context)).using(PnPLogging(LogLevel.Warning))
}
return _graph
}
is working fine.
I just cannot find anything on how to do a custom endpoint (like the one above) with pnp v3.
Can anyone help here or do I have to stick to the MSGraphClient for that purpose?

How to stop ReverseProxy from proxying request

Is there any way to prevent httputil.ReverseProxy from sending an incoming request to the target server? For example, if I have a cache and I can respond to the client using only local data. Or after validation, I want to return an error to the client.
A httputil.ReverseProxy has a single exported method, ServeHTTP(rw http.ResponseWriter, req *http.Request) which makes it implement the net/http.Handler interface.
So basically at a place you're now using an vanilla httputil.ReverseProxy instance, instead use an instance of your custom type which implements net/http.Handler as well, keeps a pointer to an instance of httputil.ReverseProxy, and either processes the request itself or calls out to that ReverseProxy instance's ServeHTTP.
You should be able to wrap the http.DefaultTransport with a cache that can either use the cache based on the request or fallback on the http.DefaultTransport.
package main
import (
"net/http"
"net/http/httputil"
)
var _ http.RoundTripper = &CachingTransport{}
type CachingTransport struct {
// put your cache here
}
func (c *CachingTransport) RoundTrip(request *http.Request) (*http.Response, error) {
// determine whether to use the cache and return, or use the default transport
return http.DefaultTransport.RoundTrip(request)
}
func main() {
_ = httputil.ReverseProxy{
Transport: &CachingTransport{},
}
}

Is it possible to run http.ListenAndServe() AND ReadFromUDP() concurrently?

I am trying to write a simple web app that will listen for UDP packets.
But I can either only listen for UDP packets, or run the web app...
I am not familiar with GoLang, but here's the code I'm using to...
listen for UDP:
ServerConn, _ := net.ListenUDP("udp", &net.UDPAddr{IP:[]byte{#,#,#,#},Port:####,Zone:""})
defer ServerConn.Close()
buf := make([]byte, 1024)
for {
n, addr, _ := ServerConn.ReadFromUDP(buf)
fmt.Println("Received ", string(buf[0:n]), " from ", addr)
}
Server logic:
package main
We import 4 important libraries
1. “net/http” to access the core go http functionality
2. “fmt” for formatting our text
3. “html/template” a library that allows us to interact with our html file.
4. "time" - a library for working with date and time.
import (
"net/http"
"fmt"
"time"
"html/template"
)
//Create a struct that holds information to be displayed in our HTML file
type Welcome struct {
Name string
Time string
}
//Go application entrypoint
func main() {
//Instantiate a Welcome struct object and pass in some random information.
//We shall get the name of the user as a query parameter from the URL
welcome := Welcome{"Anonymous", time.Now().Format(time.Stamp)}
//We tell Go exactly where we can find our html file. We ask Go to parse the html file (Notice
// the relative path). We wrap it in a call to template.Must() which handles any errors and halts if there are fatal errors
templates := template.Must(template.ParseFiles("templates/welcome-template.html"))
//Our HTML comes with CSS that go needs to provide when we run the app. Here we tell go to create
// a handle that looks in the static directory, go then uses the "/static/" as a url that our
//html can refer to when looking for our css and other files.
http.Handle("/static/", //final url can be anything
http.StripPrefix("/static/",
http.FileServer(http.Dir("static")))) //Go looks in the relative "static" directory first using http.FileServer(), then matches it to a
//url of our choice as shown in http.Handle("/static/"). This url is what we need when referencing our css files
//once the server begins. Our html code would therefore be <link rel="stylesheet" href="/static/stylesheet/...">
//It is important to note the url in http.Handle can be whatever we like, so long as we are consistent.
//This method takes in the URL path "/" and a function that takes in a response writer, and a http request.
http.HandleFunc("/" , func(w http.ResponseWriter, r *http.Request) {
//Takes the name from the URL query e.g ?name=Martin, will set welcome.Name = Martin.
if name := r.FormValue("name"); name != "" {
welcome.Name = name;
}
//If errors show an internal server error message
//I also pass the welcome struct to the welcome-template.html file.
if err := templates.ExecuteTemplate(w, "welcome-template.html", welcome); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
})
//Start the web server, set the port to listen to 8080. Without a path it assumes localhost
//Print any errors from starting the webserver using fmt
fmt.Println("Listening");
fmt.Println(http.ListenAndServe(":8080", nil));
}
taken from(https://medium.com/google-cloud/building-a-go-web-app-from-scratch-to-deploying-on-google-cloud-part-1-building-a-simple-go-aee452a2e654)
I tried putting both of these extracts in 1 file, as well as running 2 files at the same time using
go run *.go
Any help would be appreciated!
You're going to need to start looking into goroutines - since you're asking to do two things concurrently. I suggest doing some reading into channels, goroutines, and concurrency in general :)

How to get QDBusConnection::connect() failure reason

I'm trying to connect to a D-Bus signal this way:
bool result = QDBusConnection::systemBus().connect(
"foo.bar", // service
"/foo/bar", // path
"foo.bar", // interface
"SignalSomething",
this,
SLOT(SignalSomethingSlot()));
if( !result )
{
// Why!?
}
QDBusConnection::connect() returns a boolean, how do I get extended error information? If a check QDBusConnection::lastError() it returns no useful information (as QDBusError::isValid() is false).
I had the same issue and it turned out that the slot I connected to had the wrong parameter types. They must match according to Qt's documentation and it looks like connect() verifies that, despite not explicitly mentioned.
Warning: The signal will only be delivered to the slot if the parameters match.
I suggest d-feet to list signals and check their parameter types. dbus-monitor does list signals, paths and such too, but not always the exact type of parameters.
One important observation though: I fixed the issue in my particular case by using different slot parameters than the actual signal has!
I wanted to connect to a com.ubuntu.Upstart0_6 signal mentioned here to detect when the screen in Ubuntu is locked/unlocked. dbusmonitor prints the following and d-feet shows parameters (String, Array of [String])
// dbusmonitor output
signal time=1529077633.579984 sender=:1.0 -> destination=(null destination) serial=809 path=/com/ubuntu/Upstart; interface=com.ubuntu.Upstart0_6; member=EventEmitted
string "desktop-unlock"
array [
]
Hence the signal should be of type
void screenLockChangedUbuntu(QString event, QVector<QString> args) // connect() -> false
This however made connect() return false. The solution was to remove the array parameter from the slot:
void screenLockChangedUbuntu(QString event) // works
I am aware that the array parameter was always empty, but I cannot explain why it only worked when removing it.
You could try these tricks:
1) Set QDBUS_DEBUG environment variable before running your application.
export QDBUS_DEBUG=1
2) Start dbus-monitor to see what's happening on the bus. You may need to set a global policy to be able to eavesdrop system bus depending on your distro.
Update:
Are you sure connecting to the system bus succeeded? If it fails you should probably check system.conf policy and possibly create own conf in system.d. This post might be helpful.
You could first connect to the system bus with QDBusConnection::connectToBus and check if it succeeded with QDBusConnection::isConnected. Only after that you try to connect to the signal and check if that succeeded.
QDBusConnection bus = QDBusConnection::connectToBus(QDBusConnection::systemBus, myConnectionName);
if (bus.isConnected())
{
if(!bus.connect( ... ))
{
// Connecting to signal failed
}
}
else
{
// Connecting to system bus failed
}

Resources