Google GCP Trace: Trace with nested user functions information - google-app-engine

Currently this is the picture I have when analysing GCP trace. It is useful but we need to have a deeper analysis on some spots we want to improve.
What we would like to do is to have something like this
| vi/route/r
| function abc (75 ms)
| redis-hget (1 ms)
| datastore (69 ms)
| function cde (30 ms)
| ...
We have tried a lot but we didn't find further documentation regarding the concepts of GCP trace, rather only detailed API calls.
Here is our POC
Entry Level
require('#google-cloud/trace-agent').start({
projectId: 'my-project',
keyFilename: './my-credentials.json',
stackTraceLimit: 0,
});
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const toSecondLevel = require('./controllers/home');
const initController = require('./controllers/init');
app.post('/poc/', bodyParser.json(), async (req, res) => {
const trace = require('#google-cloud/trace-agent').get()
const rootSpan = trace.getCurrentRootSpan();
const { requestType } = req.body;
rootSpan.addLabel('Root - First Level' , requestType);
await toSecondLevel(rootSpan, req, res);
rootSpan.endSpan(new Date());
res.send('Hello World! ===> ' + requestType);
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
Second Level
const toThirdLevel = require('../services/home')
const secondLevelFunction = async (rootSpan, req, res) => {
await secondLevelFunctionPromise(rootSpan);
}
const secondLevelFunctionPromise = (rootSpan) => new Promise((resolve, reject) => {
const span = rootSpan.createChildSpan({ name: 'secondLevelFunction' });
setTimeout(async () => {
console.log('secondLevelFunction', span.getTraceContext())
span.addLabel('Should be nested to Root?', 'NOT nested');
await toThirdLevel(rootSpan, new Date());
span.endSpan(new Date())
resolve();
}, 2400);
})
module.exports = secondLevelFunction;
And the third Level
const thirdLevelFunction = (rootSpan, parametro1) => new Promise((resolve, reject) => {
const span = rootSpan.createChildSpan({ name: 'thirdLevelFunction' });
setTimeout(() => {
span.addLabel('thirdLevelFunction', 'not nested as well')
console.log(span.getTraceContext())
span.endSpan(new Date());
resolve();
}, 1392);
})
module.exports = thirdLevelFunction;
The problems / Conceptual questions:
1- No Span is Nested in the POC
2- All the Spans has getTraceContext(). Can I use this to some how nest my Spans?
3- Is it possible to make what I am intending to do? (A- Nest my Span and B- Nest into the same tree showed in the first image?
4- Class RootSpan extends Span. But I can't have any different behaviour from this.

Now it is working. What is needed (typescript to better understanding):
import * as traceAgent from '#google-cloud/trace-agent';
const trace: traceAgent.PluginTypes.Tracer = traceAgent.get();
const span: TraceSpan = this.trace.createChildSpan(options);
...
span.addLabel('property',yourContent);
spam.endSpan(getDate());
Important to notice that traceAgent.get() is a factory and the plugin manages to give it coherence on the order of events even if several threads are asking for new spans.
I highly recommend:
use the interfaces provided as the documentation is poor. Reading
the interfaces and actual code give you better understanding
Make a Wrapper to your span so you can create an indirection much more tied
to your needs. Now I can open a span with a label, close with a
label, add an object so the I can create a label based on the
object.

Related

React Testing Library Unit Test Case: Unable to find node on an unmounted component

I'm having issue with React Unit test cases.
React: v18.2
Node v18.8
Created custom function to render component with ReactIntl. If we use custom component in same file in two different test cases, the second test is failing with below error.
Unable to find node on an unmounted component.
at findCurrentFiberUsingSlowPath (node_modules/react-dom/cjs/react-dom.development.js:4552:13)
at findCurrentHostFiber (node_modules/react-dom/cjs/react-dom.development.js:4703:23)
at findHostInstanceWithWarning (node_modules/react-dom/cjs/react-dom.development.js:28745:21)
at Object.findDOMNode (node_modules/react-dom/cjs/react-dom.development.js:29645:12)
at Transition.performEnter (node_modules/react-transition-group/cjs/Transition.js:280:71)
at node_modules/react-transition-group/cjs/Transition.js:259:27
If I run in different files or test case with setTimeout it is working as expected and there is no error. Please find the other configs below. It is failing even it is same test case.
setUpIntlConfig();
beforeAll(() => server.listen());
afterEach(() => {
server.resetHandlers();
});
afterAll(() => {
jest.clearAllMocks();
server.close();
cleanup();
});
Intl Config:
export const setUpIntlConfig = () => {
if (global.Intl) {
Intl.NumberFormat = IntlPolyfill.NumberFormat;
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;
} else {
global.Intl = IntlPolyfill;
}
};
export const RenderWithReactIntl = (component: any) => {
return {
...render(
<IntlProvider locale="en" messages={en}>
{component}
</IntlProvider>
)
};
};
I'm using msw as mock server. Please guide us, if we are missing any configs.
Test cases:
test('fire get resource details with data', async () => {
jest.spyOn(SGWidgets, 'getAuthorizationHeader').mockReturnValue('test-access-token');
process.env = Object.assign(process.env, { REACT_APP_DIAM_API_ENDPOINT: '' });
RenderWithReactIntl(<AllocatedAccess diamUserId={diamUserIdWithData} />);
await waitForElementToBeRemoved(() => screen.getByText(/loading data.../i));
const viewResource = screen.getAllByText(/view resource/i);
fireEvent.click(viewResource[0]);
await waitForElementToBeRemoved(() => screen.getByText(/loading/i));
const ownerName = screen.getByText(/benedicte masson/i);
expect(ownerName).toBeInTheDocument();
});
test('fire get resource details with data----2', async () => {
jest.spyOn(SGWidgets, 'getAuthorizationHeader').mockReturnValue('test-access-token');
process.env = Object.assign(process.env, { REACT_APP_DIAM_API_ENDPOINT: '' });
RenderWithReactIntl(<AllocatedAccess diamUserId={diamUserIdWithData} />);
await waitForElementToBeRemoved(() => screen.getByText(/loading data.../i));
const viewResource = screen.getAllByText(/view resource/i);
fireEvent.click(viewResource[0]);
await waitForElementToBeRemoved(() => screen.getByText(/loading/i));
const ownerName = screen.getByText(/benedicte masson/i);
expect(ownerName).toBeInTheDocument();
});
Can you try these changes:
test('fire get resource details with data----2', async () => {
jest.spyOn(SGWidgets, 'getAuthorizationHeader').mockReturnValue('test-access-token');
process.env = Object.assign(process.env, { REACT_APP_DIAM_API_ENDPOINT: '' });
RenderWithReactIntl(<AllocatedAccess diamUserId={diamUserIdWithData} />);
await waitFor(() => expect(screen.getByText(/loading data.../i)).not.toBeInTheDocument());
const viewResource = screen.getAllByText(/view resource/i);
act(() => {
fireEvent.click(viewResource[0]);
});
await waitFor(() => expect(screen.getByText(/loading/i)).not.toBeInTheDocument());
expect(screen.getByText(/benedicte masson/i)).toBeVisible();
});
I've got into the habit of using act() when altering something that's visible on the screen. A good guide to here: https://testing-library.com/docs/guide-disappearance/
Using getBy* in the waitFor() blocks as above though, you may be better off specifically checking the text's non-existence.
Without seeing your code it's difficult to go any further. I always say keep tests short and simple, we're testing one thing. The more complex they get the more changes for unforeseen errors. It looks like you're rendering, awaiting a modal closure, then a click, then another modal closure, then more text on the screen. I'd split it into two or more tests.

Agora screen sharing in electron with react, Gives permission denied

I am building an electron-react app with Agora for calls. I am trying to implement the screen sharing feature.
As described in the following documentation I tried requesting screen sharing.
https://docs.agora.io/en/video/screensharing_web_ng?platform=Web#screen-sharing-on-electron
But I am getting the following error
AgoraRTCError PERMISSION_DENIED: NotAllowedError: Permission denied
I tried invoking the same function AgoraRTC.createScreenVideoTrack in react and electron but the result was the same.
How do I get permission to share the screen in electron with agora?
After some research, I found the electron way of doing it.
First I request to share the screen.
const turnScreenSharingOn = async () => {
const sources = await window.electron.media.getDesktopSources();
setScreenSources(sources);
setScreenSharePopupVisible(true);
};
window.electron.media.getDesktopSources() is a electron preload function that fetches the screen sources.
getDesktopSources: async () =>
await desktopCapturer
.getSources({
types: ['window', 'screen'],
})
.then((sources) =>
sources.map((source) => ({
id: source.id,
name: source.name,
appIconUrl: source?.appIcon?.toDataURL(),
thumbnailUrl: source?.thumbnail
?.resize({ height: 160 })
.toDataURL(),
}))
)
Once I have the sources I show the popup from which I choose the source and pass the source id to the next function.
const onScreenChoose = async (sourceId: string, cb?: () => void) => {
const stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: sourceId,
},
} as MediaTrackConstraints,
});
const track = stream.getVideoTracks()[0];
const screenTrack = AgoraRTC.createCustomVideoTrack({
mediaStreamTrack: track,
});
window.agora.screenTrack?.setEnabled(false);
window.agora.screenTrack = screenTrack;
setScreenTrack(screenTrack);
await screenShareClient.publish(screenTrack);
setScreenSharePopupVisible(false);
cb?.();
};

React Web Audio API - Play, pause and export loaded audio file

My purpose is to upload and listen to an audio file using the WEB Audio API. I have been able to listen to the audio file when selecting it, but having trouble pausing and playing it afterwards. I need to export the file to WAV format aswell. I have created a simple example, any help will be much appreciated
Loading Audio and playing it from file input
const onFileChange = (e) => {
let file = e.target.files[0];
console.log(file);
setFile(file);
let fileReader = new FileReader();
fileReader.onload = function (ev) {
audioContext.decodeAudioData(ev.target.result).then(function (buffer) {
playSound(buffer);
});
};
fileReader.readAsArrayBuffer(file);
};
Play sound using buffer source
const playSound = (buffer, time) => {
source = audioContext.createBufferSource();
source.buffer = buffer;
source.connect(audioContext.destination);
source.start(time);
setIsPlaying(true);
};
I'm facing problem here with pausing and playing:
const onPlayPause = (e) => {
console.log("audioState", audioContext.state);
console.log("duration", audioContext.currentTime);
if (!isPlaying) {
//source.start();
setIsPlaying(true);
} else if (audioContext.state === "running") {
setPlayDuration(audioContext.currentTime);
audioContext.suspend();
setIsPlaying(false);
} else if (audioContext.state === "suspended") {
audioContext.resume();
}
};
Export Audio:
const exportAudioFile = () => {
offlineContext.render().then((buffer) => {
setRenderState('encoding');
const handleMessage = ({ data }) => {
var blob = new window.Blob([new DataView(data)], {
type: 'audio/wav',
});
//blob = new Blob([buffer], { type: "audio/wav" });
const url = window.URL.createObjectURL(blob);
}
window.URL.revokeObjectURL(url);
})};
Codesandboxlink:
https://codesandbox.io/s/react-audiocontext-pause-play-fw20u?file=/src/App.js
I've had my fair share of headaches with persisting things in react functional components. Fortunately, useRef is an awesome tool for just that:
https://reactjs.org/docs/hooks-reference.html#useref
As the documentation says, it essentially returns a container which .current property persists across re-renders.
I forked your code to useRef in action:
https://codesandbox.io/s/react-audiocontext-pause-play-forked-si59u?file=/src/App.js:120-159
Basically, when you load the file, store your shiny new AudioContext in the ref's .current field, and reference that throughout the rest of your component. You can clean it up a bit, IE store the .current in a constant scoped to the function you're using it in.
Two key spots:
export default function App() {
const audioCtxContainer = useRef(null);
...
and
audioCtxContainer.current = new AudioContext();
audioCtxContainer.current
.decodeAudioData(ev.target.result)
.then(function (buffer) {
playSound(buffer);
});
useRef is useful for any mutable object that you want to persist for the lifetime of the component.
Let me know if that helps!

Creating relative URLs with `new URL()` behaves differently when first param is a variable. WHY?

I'm trying to implement web workers in NextJs, I've followed their example but It really bugs me that I cannot pass the worker relative URL as a variable to new URL(url, baseUrl).
The following snippet is where the worker gets called:
import { useEffect, useRef, useCallback } from 'react'
export default function Index() {
const workerRef = useRef()
useEffect(() => {
const workerUrl = '../worker.js';
console.log({
URL: new URL('../worker.js', import.meta.url),
meta: import.meta.url
});
console.log({
URL: new URL(workerUrl, import.meta.url),
meta: import.meta.url
});
workerRef.current = new Worker(new URL('../worker.js', import.meta.url))
workerRef.current.onmessage = (evt) =>
alert(`WebWorker Response => ${evt.data}`)
return () => {
workerRef.current.terminate()
}
}, [])
const handleWork = useCallback(async () => {
workerRef.current.postMessage(100000)
}, [])
return (
<div>
<p>Do work in a WebWorker!</p>
<button onClick={handleWork}>Calculate PI</button>
</div>
)
}
This strangely logs:
{
"URL":"/_next/static/media/worker.3c527896.js",
"meta":"file:///home/omar/CODE/NextJs/lullo/with-web-worker-app/pages/index.js"
}
{
"URL":"file:///home/omar/CODE/NextJs/lullo/with-web-worker-app/worker.js",
"meta":"file:///home/omar/CODE/NextJs/lullo/with-web-worker-app/pages/index.js"
}
How in the world is this any different:
const workerUrl = '../worker.js';
console.log({
URL: new URL('../worker.js', import.meta.url),
meta: import.meta.url
});
console.log({
URL: new URL(workerUrl, import.meta.url),
meta: import.meta.url
});
The problem is that I cannot pass the URL as a prop, to some generic worker caller. I get the annoying error:
SecurityError: Failed to construct 'Worker': Script at 'file:///home/omar/CODE/NextJs/lullo/client/src/utils/WebWorkers/postErrorToServer.ts' cannot be accessed from origin 'http://localhost:3000'.
This is probably happening because in the first case:
const workerUrl = '../worker.js';
const url = new URL(workerUrl, import.meta.url);
webpack sees the URL as dynamic and is unable to properly bundle the web worker at compile-time. Something similar happens if you define the worker as follows:
const url = new URL('../worker.js', import.meta.url);
const worker = new Worker(url);
This comment on a discussion in webpack's GitHub repo might help in your case. I don't think the worker URL can be truly dynamic, due to the above reason - webpack needs to know the url of the worker script at compile-time.

Re-rendering of Google Maps Elevations API responses within React functional component

I am writing an app for vehicle tracking. With the help of Google Maps API, I am able to get directions and extract all the required info. The problem appeared with Elevations API responses. From DirectionRender class I am sending path and distance as props. GM Elevations request is done via elevator.getElevationAlongPath(option,PlotElevation). PlotElevation (elevations,status) is a callback function. However, no matter how I try to receive just one response from it (using useMemo, useEffect, I think I tried everything), still, there are problems with the re-rendering of responses. OVER_QUERY_LIMIT or endless re-render. Could someone help with that?
Thanks
const Elevation = React.memo(props =>{
const [path, setPath] = useState({...props.path})
const [distance, setDistance]=useState({...props.distance})
const [elevationArray, setElevationArray] = useState(null)
const [stop, setStop] = useState(false)
let pathElev = JSON.parse(JSON.stringify(path))
React.useEffect(() => {
setPath(props.path)
}, [props.path])
React.useEffect(() => {
setDistance(props.distance)
}, [props.distance])
let elevator = new window.google.maps.ElevationService;
let numberSamples = parseInt( distance/40)
let options = {
'path':path,
'samples':numberSamples
}
//The problem starts here
const PlotElevation = (elevations, status) => {
if (stop === false){
console.log('status',status)
console.log(JSON.parse(JSON.stringify(elevations)))
setElevationArray(elevations)
//setStop(true)
console.log(elevations[19].elevation)
return
}
}
const Memo = React.useMemo(
()=>{
elevator.getElevationAlongPath(
options,PlotElevation
)
},[elevator.getElevationAlongPath(
options,PlotElevation
)])
// elevator.getElevationAlongPath(
// {
// path: path,
// samples: 100
// }, elevations =>{
// setElevationArray({
// // We’ll probably want to massage the data shape later:
// // elevationArray: elevations
// })
// }
// )
return (
<div>
{Memo}
{console.log('path is received ', pathElev)}
{console.log('number of samples', numberSamples)}
{console.log('elevation check ',elevationArray)}
{/* {elevator.getElevationAlongPath(
options,PlotElevation)} */}
</div>
)
})
export default Elevation

Resources